1. 类产生多个实例对象
1.1 类对象提供默认行为
Python 类主要特性的要点:
- class 语句创建类对象并将其赋值给变量名。
- class 语句内的赋值语句会创建类的属性。
- 类属性提供对象的状态和行为。
1.2 实例对象是具体的元素
类的实例内含的重点概要:
- 像函数那样调用类对象会创建新的实例对象。
- 每个实例对象继承类的属性并获得了自己的命名空间。
- **在方法内对 self 属性做赋值运算会产生每个实例自己的属性。**在类方法函数内,第一个参数(self)会引用正处理的实例对象。
1.3 第一个例子
1 2 3 4 5
| class FirstClass: def setdata(self, value): self.data = value def display(self): print(self.data)
|
位于类中的函数通常称为方法。方法是普通 def。在方法函数中,调用时,第一个参数自动接收隐含的实例对象:调用的主体。
1 2
| x = FirstClass() y = FirstClass()
|
如果对实例以及类对象内的属性名称进行点号运算,Python 会通过继承搜索从类取得变量名:
1 2
| x.setdata('King Arthur') y.setdata(3.14159)
|
x 或 y 本身都没有 setdata 属性,Python 会顺着实例到类的连接搜索。继承是在属性点号运算时发生的,而且只与查找连接对象内的变量名有关。
1 2
| x.display() y.display()
|
King Arthur
3.14159
在类内时,通过方法内对 self 进行赋值运算;而在类外时,则可以通过对实例对象进行赋值运算:
1 2
| x.data = 'New value' x.display()
|
New value
可以在实例命名空间内产生全新的属性:
2. 类通过继承进行定制
类可以引入新组件(子类)来进行修改,而不对现有组件进行原地的修改。由类产生的实例对象会继承该类的属性。
属性继承机制的核心观点:
- **超类列在了类开头的括号中。**含有继承的类称为子类,而子类所继承的类就是其超类。
- 类从超类中继承属性。
- 实例会继承所有可读取类的属性。
- 每个 object.attribute 都会开启新的独立搜索。
- **逻辑的修改是通过创建子类,而不是修改超类。**在树中层次较低的子类中重新定义超类的变量名,子类就可取代并制定所继承的行为。
2.1 第二个例子
定义一个新的类 SecondClass,继承 FirstClass 所有变量名,并提供其自己的一个变量名:
1 2 3
| class SecondClass(FirstClass): def display(self): print('Current value = "%s"' % self.data)
|
1 2 3
| z = SecondClass() z.setdata(42) z.display()
|
Current value = "42"
SecondClass 引入的专有化完全是在 FirstClass 外部完成的,不会影响当前存在的或未来存在的 FirstClass 对象:
New value
2.2 类是模块内的属性
类名称总是存在于模块中,class 语句会在导入时执行已定义的变量名,而这些变量名会变成独立的模块属性。
3. 类可以截获 Python 运算符
运算符重载就是让用类写成的对象,可截获并响应用在内置类型上的运算:加法、切片、打印和点号运算等。
重载运算符主要概念的概要:
- **以双下划线命名的方法(
__x__
)是特殊的 hook。**Python 语言替每种运算和特殊命名的方法之间,定义了固定不变的映射关系。
- **当实例出现在内置运算时,这类方法会自动调用。**例如,如果实例对象继承了
__add__
方法,当对象出现在 + 表达式内时,该方法就会调用。
- 类可覆盖多数内置类型运算。
- 运算符覆盖方法没有默认值,而且也不需要。
- 运算符可让类与 Python 的对象模型相集成。
运算符重载是可选的功能,除非类需要模仿内置类型接口,不然应该使用更简单的命名方法。
3.1 第三个例子
定义 SecondClass 的子类,实现三个特殊名称的属性,让 Python 自动进行调用:
- 当新的实例构造时,会调用
__init__
(self 是新的 ThirdClass 对象)。
- 当 ThirdClass 实例出现在 + 表达式中时,则会调用
__add__
。
- 当打印一个对象的时候,运行
__str__
。
1 2 3 4 5 6 7 8 9
| class ThirdClass(SecondClass): def __init__(self, value): self.data = value def __add__(self, other): return ThirdClass(self.data + other) def __str__(self): return '[ThirdClass:%s]' % self.data def mul(self, other): self.data *= other
|
1 2
| a = ThirdClass('abc') a.display()
|
Current value = "abc"
[ThirdClass:abc]
1 2
| b = a + 'xyz' b.display()
|
Current value = "abcxyz"
[ThirdClass:abcxyz]
[ThirdClass:abcabcabc]
4. 世界上最简单的 Python 类
1 2 3 4 5 6
| class rec: pass
rec.name = 'Bob' rec.age = 40
print(rec.name)
|
Bob
类只是独立完备的命名空间,只要有类的引用值,就可以在任何时刻设定或修改其属性。
1 2 3
| x = rec() y = rec() x.name, y.name
|
('Bob', 'Bob')
1 2
| x.name = 'Sue' rec.name, x.name, y.name
|
('Bob', 'Sue', 'Bob')
命名空间对象的属性通常是以字典的形式实现的。__dict__
属性是针对大多数基于类的对象的命名空间字典:
1
| list(rec.__dict__.keys())
|
['__module__', '__dict__', '__weakref__', '__doc__', 'name', 'age']
['name']
[]
x 有自己的 name,y 依然是空的。
方法也可以完全独立地在任意类对象的外部创建:
1 2 3 4
| def uppername(obj): return obj.name.upper()
uppername(x)
|
'SUE'
当把这个简单函数赋值成类的属性,就会变成方法,可以由任何实例调用:
1 2
| rec.method = uppername x.method()
|
'SUE'
'BOB'
4.1 类与字典的关系
利用类来记录属性,可以用不同的实例记录不同的数据,模拟字典的方法:
1 2 3 4
| class rec: pass rec.name = 'mel' rec.age = 45 rec.job = 'trainer/writer'
|