1. class 语句
Python 的 class 并不是声明式的,class 语句是对象的创建者并且是一个隐含的赋值运算。
1.1 一般形式
class 是复合语句。在头一行中,超类列在类名称之后的括号内,由逗号相隔。列出一个以上的超类会引起多重继承:
1 2 3 4 class name(superclass, ...): # 赋值给变量名 data = value def method(self, ...): self.member = value
1.2 例子
当 Python 执行 class 语句时(不是调用类),会从头至尾执行其主体内的所有语句。在这个过程中,进行的赋值运算会在这个类作用域中创建变量名,从而成为对应的类对象内的属性。类就像模块和函数:
就像函数一样,class 语句是本地作用域,由内嵌的赋值语句建立的变量名,就存在于这个本地作用域内。
就像模块内的变量名,在 class 语句内赋值的变量名会变成类对象中的属性。
在 class 语句内赋值的变量名,会创建类属性,而内嵌的 def 则会创建类方法,但是,其他赋值语句也可制作属性。
例如,把简单的非函数的对象赋值给类属性,就会产生数据属性 ,由所有实例共享:
1 2 3 4 5 6 class SharedData : spam = 42 x = SharedData() y = SharedData() x.spam, y.spam
(42, 42)
1 2 SharedData.spam = 99 x.spam, y.spam, SharedData.spam
(99, 99, 99)
1 2 x.spam = 88 x.spam, y.spam, SharedData.spam
(88, 99, 99)
对实例的属性进行赋值运算会在该实例内创建或修改变量名,而不是在共享的类中。
1 2 3 4 5 6 7 8 9 10 class MixedNames : data = 'spam' def __init__ (self, value ): self.data = value def display (self ): print (self.data, MixedNames.data) x = MixedNames(1 ) y = MixedNames(2 ) x.display(); y.display()
1 spam
2 spam
2. 方法
方法位于 class 语句的主体内,是由 def 语句建立的函数对象。方法的第一个参数总是接收方法调用的隐形主体,也就是实例对象。
在类方法中,按惯例第一个参数通常都称为 self。这个参数给方法提供了一个 hook,从而返回调用的主体,也就是实例对象。
2.1 例子
1 2 3 4 class NextClass : def printer (self, text ): self.message = text print (self.message)
1 2 3 x = NextClass() x.printer('instance call' ) x.message
instance call
'instance call'
2.2 调用超类构造函数
在构造时,Python 会找出并且只调用一个 __init__
。如果要保证子类的构造函数也会执行超类构造时的逻辑,一般都必须通过类明确地调用超类的 __init__
方法:
1 2 3 4 5 6 7 8 9 10 class Super : def __init__ (self, x ): pass class Sub (Super ): def __init__ (self, x, y ): Super.__init__(self, x) pass I = Sub(1 , 2 )
3. 继承
当对对象进行点号运算时,就会发生继承,而且涉及了搜索属性定义树(一个或多个命名空间)。
3.1 属性树的构造
实例属性是由对方法内 self 属性进行赋值运算而生成的。
类属性是通过 class 语句内的语句(赋值语句)而生成的。
超类的连接是通过 class 语句首行的括号内列出类而生成的。
3.2 继承方法的专有化
继承会现在子类寻找变量名,然后才查找超类,子类就可以对超类的属性重新定义来取代默认的行为。
1 2 3 4 5 6 7 8 9 10 11 12 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' ) x = Super() x.method()
in Super.method
starting Sub.method
in Super.method
ending Sub.method
3.3 抽象超类
类的部分行为默认是由其子类所提供的。如果预期的方法没有在子类中定义,当继承搜索失败时,Python 会引发未定义变量名的异常。
4. 命名空间:完整的内容
无点号运算的变量名与作用域相对应。
点号的属性名使用的是对象的命名空间。
有些作用域会对对象的命名空间进行初始化(模块和类)。
4.1 简单变量名:如果赋值就不是全局变量
无点号的简单变量名遵循 LEGB 作用域法则:
**赋值语句(X = value)。**使变量名成为本地变量:在当前作用域内,创建或改变变量名 X,除非声明它是全局变量。
**引用(X)。**在当前作用域内搜索变量名 X,之后是在任何以及所有的嵌套的函数中,然后是在当前的全局作用域中搜索,最后在内置作用域中搜索。
4.2 属性名称:对象命名空间
**赋值语句(object.X = value)。**在进行点号运算的对象的命名空间内创建或修改属性名 X。继承树的搜索只发生在属性引用时,而不是属性的赋值运算时。
**引用(object.X)。**就基于类的对象而言,会在对象内搜索属性名 X,然后是其上所有可读取的类。对于不是基于类的对象而言,则是从对象中直接读取 X。
4.3 嵌套类
类有时在函数中进行嵌套,并生成,这是闭包的一种变体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 X = 1 def nester (): print (X) class C : print (X) def method1 (self ): print (X) def method2 (self ): X = 3 print (X) I = C() I.method1() I.method2() print (X) nester() print ('-' * 40 )
1
1
1
1
3
----------------------------------------
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 X = 1 def nester (): X = 2 print (X) class C : print (X) def method1 (self ): print (X) def method2 (self ): X = 3 print (X) I = C() I.method1() I.method2() print (X) nester() print ('-' * 40 )
1
2
2
2
3
----------------------------------------
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 X = 1 def nester (): X = 2 print (X) class C : X = 3 print (X) def method1 (self ): print (X) print (self.X) def method2 (self ): X = 4 print (X) self.X = 5 print (self.X) I = C() I.method1() I.method2() print (X)nester() print ('-' * 40 )
1
2
3
2
3
4
5
----------------------------------------
4.4 命名空间字典
属性点号运算其实内部就是字典的索引运算,而属性继承其实就是搜索链接的字典。
1 2 3 4 5 6 7 class Super : def hello (self ): self.data1 = 'spam' class Sub (Super ): def hola (self ): self.data2 = 'eggs'
{}
__main__.Sub
(__main__.Super,)
(object,)
属性最后会位于实例的属性命名空间字典内,而不是类。
1 2 3 Y = Sub() X.hello() X.__dict__
{'data1': 'spam'}
{'data1': 'spam', 'data2': 'eggs'}
dict_keys(['__module__', 'hola', '__doc__'])
dict_keys(['__module__', 'hello', '__dict__', '__weakref__', '__doc__'])
{}
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'data1',
'data2',
'hello',
'hola']
4.5 命名空间链接:树爬升
可以用实例和类的特殊属性 __class__
和 __bases__
来显示类树:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 """ Climb inheritance trees using namespace links, displaying higher superclasses with indentation for height """ def classtree (cls, indent ): print ('.' * indent + cls.__name__) for supercls in cls.__bases__: classtree(supercls, indent + 3 ) def instancetree (inst ): print ('Tree of %s' % inst) classtree(inst.__class__, 3 ) def selftest (): class A : pass class B (A ): pass class C (A ): pass class D (B, C): pass class E : pass class F (D, E): pass instancetree(B()) instancetree(F()) if __name__ == '__main__' : selftest()
Tree of <__main__.selftest.<locals>.B object at 0x0000022E079E8BA8>
...B
......A
.........object
Tree of <__main__.selftest.<locals>.F object at 0x0000022E079E8BA8>
...F
......D
.........B
............A
...............object
.........C
............A
...............object
......E
.........object
5. 类与模块的关系
模块
是数据/逻辑包。
通过编写 Python 文件或 C 扩展来创建。
通过导入来使用。
类
实现新的对象。
由 class 语句创建。
通过调用来使用。
总是位于一个模块中。