第二十九章 类代码编写细节

class语句

就像def一样,class语句是对象的创建者并且是一个隐含的赋值运算:当他执行时会产生类对象,并把其引用值储存到名称中。此外像def一样,class语句也是可执行代码。直到python抵达并运行定义的class语句前,你的类是不存在的。

一般形式

class是复合语句,其缩进语句的主体一般都出现在头部行下边。在头部行中,父类列在类名称之后的口号内,由逗号相隔。列出一个以上的父类会触发多继承。下面是class语句的一般形式:

class name(superclass,....):
	attr = value
	def method(self,....)
		self.attr = value

示例

类既像模块也像函数:

  • 就想函数一样,由class语句中内嵌的赋值语句创建的名称,位于class的局部作用域中;
  • 就像模块内的名称,在class语句中赋值的名称会成为类对象中的属性;
class ShareData():
	spam = 42

x = ShareData()
y = ShareData()

print(x.spam, y.spam)		# output:(42, 42)

在这里,名称spam是在class语句的顶层被赋值,因此会被附加在这个类中,从而为所有的实例共享。我们可以通过类名修改它:

ShareData.spam = 99
print(x.spam, y.spam)		# output:(99, 99)

所以像这种类属性可以用于管理横跨所有实例的信息,同时通过实例的修改的是实例中的变量:

x.spam = 88
print(x.spam, y.spam)		# output:(88, 99)

命名空间的先后顺序是:实例、子类、父类…

方法

从程序设计的角度看,方法和函数的工作方式是一样的,只是有一个重要的差异,:方法的第一位参数总是接受方法调用的隐含主体,也就是当前的实例对象;

instance.method(args....)

上述方法调用会被自动翻译为:

instance.method(instance, args....)

下面的代码为了说明方法定义时的self时什么:

>>> class Aclass:
...     def printer(self, text):
...             self.text = text
...             print(self)
...             print(self.text)
... 
>>> x = Aclass()
>>> x.printer('abc')
<__main__.Aclass object at 0x10b6bc290>
abc
>>> print(x)
<__main__.Aclass object at 0x10b6bc290>

调用父类构造函数

class super():
	def __init__(self, x):
		...default code...

class sub(super):
	def __init__(self, x, y)
		super.__init__(self, x)
		...custom code...	

继承

属性数的构造

通常来说:

  • 实例属性是由对方法内的self属性进行赋值运算而产生的;
  • 类属性是通过class语句内的语句(赋值语句)而创建的;
  • 父类的连续是通过class语句首行的括号内列出的类而产生的;

在这里插入图片描述

定制被继承的方式

class Super:
	def method(self):
		print('in super.method')

class Sub(Super):
	def method(self):
		print('starting Sub.method')
		Super.method(self)
		print('ending Sub.method')

直接调用父类方法是这里的关键,Sub类用其定制化版本替代了Super中的方法函数。但在替代时,Sub又调用了Super所提供的版本,从而完成了默认的行为。换句话说,Sub。method只是扩展了Super.method的行为,而不是完全替代了它;

类接口技术

class Super():
	def method(self):
		print('in Super.method')
	
	def delegate(self):
		self.action()

class Inheritor(Super):
	pass

class Replacer(Super):
	def method(self):
		print('in Replacer.method')

class Extender(Super):
	def method(self):
		print('syarting Extender.method')
		Super.method(self)
		print('ending Extender.method')

class Provider(Spuer):
	def action(self):
		print('in Provider.action')

if __name__ == '__main__':
	for klass in (Inheritor, Replacer, Extender):
		print('\n' + klass.__name__ + '...')
		klass().method()
	print('\nProvider')
	x = Provider()
	x.delegate()

output:

Inheritor...
in Super.method

Replacer...
in Replacer.method

Extender...
syarting Extender.method
in Super.method
ending Extender.method

Provider
in Provider.action

抽象父类

上面的代码中,有一点是需要额外理解的。当我们通过Provider实例调用delegate方法时,会发生两次独立的继承搜索:

  1. 在x.delegate调用一开始,python会搜索Provider实例和类树中更上一层的类对象,直到Super中找到delegate的方法。实例x会照常传给该方法的self参数;
  2. 在 Super. delegate方法中,self.action会对self以及它上层的对象发起另一次新的独立继承搜索。因为self引用了一个 Provider实例,所以 action方法会在Provider子类中找到;

这种‘填补空缺’的代码结构正是典型的OOP的软件框架;
类的编写者有时使用assert语句,使这种子类要求更为明显,或者引发内置的NotImplementedError异常,这个将在后续学习;

命名空间:结论

  • 无点号运算的名称对应于作用域;
  • 掉点号的属性名使用的是对象的命名空间;
  • 有些作用域用于初始化对象的命名空间;

简单名称:如果被赋值就不是全局的

无点号的简单名称遵循LEGB词法作用域规则:

  • 赋值语句(X = value):默认情况下名称成为局部的
  • 引用(X):按照LEGB规则;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值