类生成多个实例对象
类对象来自语句,而实例来自调用。每次调用一个类,就会得到这个类的实例;
类对象提供默认行为
以下是python类的主要特性:
- class语句创建类对象并将其赋值给一个名称(与def语句类似);
- class语句内的赋值语句会创建类的属性;
- 类属性提供了对象的状态和行为;
实例对象是具体的元素
以下是累的实例的重点概要:
- 像函数那样调用类对象会创建新的实例对象;
- 每个实例对象继承了可类的属性并获取了自己的命名空间;
- 在方法内对self属性做赋值运算会产生每个实例自己的属性;
第一个示例
class FirstClass:
def setdata(self, value):
self.value = value
def display(self):
print(self.data)
位于类中的函数通常称为方法。我们需要创建一些类的实例来理解其工作原理:
x = FirstClass()
y = FirstClass()
这两个实例一开始是空的,但是它们被连接到创建它们的类。如果对实例以及类对象内的属性名称做点好运算,python就会通过继承搜索访问类中的名称:
x.setdata('king arthur')
y.setdata(3.1415926)
x和y本身都没有setdata属性,为了寻找这个属性,python会顺着实例到类的连接搜索。而这就是所谓的python的继承:继承是在属性点号运算时发生的,而且只与查找连接对象内的名称有关,在这里遵循的是上图的is-a连接;
x.display() # output:king arthur
y.display() # 3.1415926
另一种理解该模型的动态性的方式是,我们可以在类的内部或外部修改实例属性。在类的内部,通过方法对self进行赋值运算;而在类的外部,则可以通过对实例对象进行赋值运算:
x.data = 'New value'
x.display() # New value
可以通过在类方法函数外对变量名进行赋值运算,从而在实例命名空间内产生全新的属性:
x.anthername = 'spam'
通过上面的语句可以增加一个名为anthername的新属性。
类通过继承进行定制
以下是属性继承机制的核心观点:
- 父类列在class语句头部的括号中;
- 类从其父类中继承属性;
- 实例会继承所有可访问类的属性;
- 每一个object.attribute引用都会启动一个新的独立的搜索;
- 逻辑的修改是通过创建子类,而不是修改父类;
第二个示例
定义一个新的类SecondClass,继承FirstClass所有变量名:
class SecondClass(FirstClass):
def display(self):
print('Current value = "%s" % self.data')
继承搜索会从实例往上进行,首先到子类,然后到父类,直到所找的属性名称首次出现为止。因为在SecondClass中又重新定义了display方法,并且它会被首先找到。我们把这种在树中较低处发生的通过重新定义取代属性的动作称为重载。
z = SecondClass()
z.setdata(42)
z.display() # output:Current value = "42"
虽然在SecondClass中重新定义了display方法,其修改不会影响到FirstClass类中的display方法;
类是模块内的属性
类可以截获python运算符
现在,让我们来看类和模块的第三个也是最后一个主要差别:运算符重载。简而言之,运算符重载就是让用类编写的对象,可截获并响应应用在内置类型上的运算:加法、切片、打印和点号运算等。
这其实是一种自动分发机制:表达式和其他内置运算被路由到了类内部的实现。在这点上类和模块也基本不同:模块可以实现函数调用,但却不能实现表达式的行为。
以下是重载运算符主要概念的概要:
- 以双下划线命名的方法(__X __)是特殊钩子;
- 当实例出现在内置运算中时,这类方法会自动被调用;
- 类可以重载绝大多数内置类型运算;
- 默认的运算符重载方法既不存在,也不需要;
- 新式类有一些默认的运算符重载方法,但是不属于常见运算;
- 运算符将与python的对象模型结合到一起
第三个示例
我们继续定义SecondClass的子类,实现python会自动调用的三个特殊名称属性:
- __init __会在创建新的实例时被调用;
- __add __会在ThirdClass实例出现在+表达式中时被调用;
- __str __会在打印一个对象的时候被调用;
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
a = ThirdClass('abc')
a.display() # output: Current value = "abc"
print(a) # output: [ThirdClass: abc]
b = a + 'xyz'
b.display() # output: Current value = "abcxyz"
print(b) # output: [ThirdClass: abcxyz]
为什么要使用运算符重载
在设计类时运算符重载并不是必须的,坦率地说,只有在实现具有数学本质的对象时,才会用到许多运算符重载方法。
世界上最简单的python类
下列语句建立一个类,其中完全没有附加的属性(一个空的命名空间):
class rec: pass
后续通过赋值变量名给这个类增加属性:
rec.name = 'Bob'
rec.age = 40
通过赋值语句创建这些属性后,就可以用一般的语句访问它们:
x = rec()
y = rec()
print(x.name) # output: 'Bob'
print(y.name) # output: 'Bob'
使用__dict __获得类中所有的属性:
>>> rec.__dict__
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'rec' objects>, '__weakref__': <attribute '__weakref__' of 'rec' objects>, '__doc__': None, 'name': 'Bob', 'age': 40})
>>>
>>>
>>> list(rec.__dict__.keys())
['__module__', '__dict__', '__weakref__', '__doc__', 'name', 'age']
使用__class __获取指向其类的链接:
>>> x.__class__
<class '__main__.rec'>
使用__bases __获取父类对象引用的元组:
>>> rec.__bases__
(<class 'object'>,)