Tips 28
python类中包含很多特殊方法,这些方法让类用起来如同序列、字典、函数、迭代器,甚至像个数字。
基础知识
最常见的就是__init__()。
序号 | 目的 | 代码 | python内部调用 |
---|---|---|---|
1 | 初始化一个实例 | x = MyClass() | x.__init__() |
2 | 字符串的“官方”表现形式 | repr(x) | x.__repr__() |
3 | 字符串的“非正式"值 | str(x) | x.__str__() |
4 | 字节数组的”非正式“值 | bytes(x) | x.__bytes__() |
5 | 格式化字符串的值 | format(x, format_spec) | x.__format__(format_spec) |
1.对__init__()方法调用发生在实例被创建之后。如果要控制实际创建进程,请使用__new__方法。 2.__repr__()方法返回的字符串为合法的python表达式 3.在调用print(x)的同时调用了__str__()方法
行为方式和迭代器类似的类
序号 | 目的 | 编写的代码 | python的实际调用 |
---|---|---|---|
1 | 遍历每个序列 | iter(seq) | seq.__iter__() |
2 | 迭代器取下一个值 | next(seq) | seq.__next__() |
3 | 按逆序创建一个迭代器 | reversed(seq) | seq.__reversed__() |
1.无论何时创建迭代器都将调用__iter__()方法。这是用初始值对迭代器进行初始化的绝佳之处。 2.无论何时从迭代器中获取下一个值都将调用__next__()方法。 3.__reversed__()方法并不常用。它以一个现有序列为参数,并将该序列中所有元素从尾到头以逆序生成一个新的迭代器。
属性
序号 | 目的 | 编写的代码 | python 实际调用 |
---|---|---|---|
1 | 获取一个属性 | x.my_property | x.__getattribute__('my_property') |
2 | 获取一个属性(备用) | x.my_property | x.__getattr__('my_property') |
3 | 设置某属性 | x.my_property = value | x.__setattr__('my_property', value) |
4 | 删除某属性 | del x.myproperty | x.__delattr__('my_property') |
5 | 列出所有属性和方法 | dir(x) | x.__dir__() |
1.如果类定义了__getattribute__()方法,在每次引用属性或方法名称时python都调用它(特殊方法名称除外,因为会导致无限循环) 2.如果类定义了__getattr__()方法,python在正常位置查询属性时才会调用。 3.无论何时给属性赋值,都会调用__setattr__() 4.删除属性时,会调用__delattr__() 5.如果定义了__getattr__()或__getattribute__(),__dir__()方法将会非常有用
__getattr__()和__getattribute__()方法的区别非常细微,但非常重要。
class Dynamo:
def __getattr__(self, key):
if key == 'color':
return 'PapayaWhip' # 属性名称以字符串的形式传入__getattr()__方法
else: # 如果属性名称未知,触发异常
raise AttributeError
>>> dyn = Dynamo()
>>> dyn.color # dyn没有名为color的属性,获取时将调用__getattr__()
'PapayaWhip'
>>> dyn.color = 'LemonChiffon' # 显示的设置了color属性后,不再调用__getattr__方法。
>>> dyn.color
'LemonChiffon'
另一方面,__getattribute__()方法是直接的,无条件的。
class SuperDynamo:
def __getattribute__(self, key):
if key == 'color':
return 'PapayaWhip'
else:
raise AttributeError
>>> dyn = SuperDynamo()
>>> dyn.color # 直接调用__getattribute__(),返回color
'PapayaWhip'
>>> dyn.color = 'LemonChiffon'
>>> dyn.color # 虽然显示的设置了color属性,但仍然会调用__getattribute__()
'PapayaWhip'
如果定义了类的__getattribute__()方法,你可能还想定义一个__setattr__()方法,需要两则之间进行协同,以跟踪属性的值。 必须特别小心__getattribute__()方法,因为python在查找类的方法名称时也将对其进行调用
class Rastan:
def __getattribute__(self, key):
raise AttributeError # 定义了一个总是触发异常的__getattribute__方法
def swim(self):
pass
>>> hero = Rastan()
>>> hero.swim() # 调用hero.swim()时,python将在Rastan类查找swim()方法。该查找将执行整个__getattribute__()方法,因为所有的属性和方法查找都通过__getattribute__()方法。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __getattribute__
AttributeError
行为方式和函数类似的类
可以让类的实例变得可调用-就想函数可以调用一样-通过定义__call__()方法
序号 | 目的 | 编写代码 | Python实际调用 |
---|---|---|---|
像调用函数一样使用实例 | my_instance() | my_instance.__call__() |
行为和序列类似的类
如果类作为一系列值的容器出现,可以通过下面方法,让其的行为方式和序列类似。
目的 | 代码 | python实际调用 |
---|---|---|
序列的长度 | len(seq) | seq.__len__() |
判断序列是否包含指定的值 | x in seq | seq.__contains__(x) |
行为和字典类似的类
类可以像字典一样根据键来返回值
目的 | 代码 | python实际调用 |
---|---|---|
通过键来获取值 | x[key] | x.__getitem__(key) |
通过键来设置值 | x[key] = value | x.__setitem__(key, value) |
删除一个键值对 | del x[key] | x.__delitem__(key) |
为缺失键提供默认值 | x[nonexistent_key] | x.__missing__(nonexistent_key) |
行为和数值类似的类
使用适当的特殊方法,可以将类的行为定义为与数字相仿。也就是说,可以进行相加、相减、及其他运算,Fraction类就实现了这些特殊方法:
>>> from fractions import Fraction
>>> x = Fraction(1,3)
>>> x/3
Fraction(1,9)
| 目的 | 编写的代码 | python实际调用 | | 加法 | x + y | x.__add__(y) | | 减法 | x - y | x.__sub__(y) | | 乘法 | x * y | x.__mul__(y) | | 除法 | x / y | x.__truediv__(y) | | 地板除 | x//y | x.__fooldiv__(y) | | 取余 | x % y | x.__mod__(y) | | 地板除&取余 | divmod(x,y) | x.__divmod__(y)| | 乘幂 | x ** y | x.__pow__(y) | | 左移位 | x << y | x.__lshift__(y) | | 右移位 | x >> y | x.__rshift__(y) | | 按位与 | x & y | x.__and__(y) | | 按位异或 | x^y | x.__xor__(y) | | 按位或 | x | y | x.__or__(y) |
前面是第一个的操作数调用对应函数,第二个操作数作为参数,下面的特殊方法集合,是第二个操作数调用方法,第一个操作数作为参数。
| 目的 | 代码 | python 调用 | | + | x + y | y.__radd__(x) | | - | x - y | y.__rsub__(x) | | * | x * y | y.__rmul__(x) | | / | x / y | y.__rturediv__(x) | | // | x // y | y.__rfloordiv__(x) | | % | x % y | y.__rmod__(x) | |// & % | divmod(x,y) | y.__rdivmod__(x) | | | x y | y.__rpow__(x) | | << | x << y | y.__rlshift__(x) | | >> | x >> y | y.__rrshift__(x) | | & | x & y | y.__rand__(x)| | ^ | x ^ y | y.__rxor__(x) | | 或 | x or y | y.__ror__(x) |
如果原地操作呢?x += 3
| 代码 | python实际调用 | | x += y | x.__iadd__(y) | | x -= y | x.__isub__(y) | | x *= y | x.__imul__(y) | | x /= y | x.__iturediv__(y) | | x //= y | x.__ifloordiv__(y) | | x %= y | x.__imod__(y) | | x **= y | x.__ipow__(y) | | x <<= y | x.__ilshift__(y) | | x >>= y | x.__irshift__(y) | | x &= y | x.__iland__(y) | | x ^= y | x.__ixor__(y) | | x |= y | x.__ior__(y) |
还有一些一元运算符,可以对“类-数字”对象自己执行。
| 目的 | 代码 | python实际调用 | | 负数 | -x | x.__neg__() | | 正数 | +x | x.__pos__() | | 绝对值 | abs(x) | x.__abs__()| | 取反 | ~x | x.__invert__() | | 复数 | complex(x) | x.__complex__()| | 整数转换 | int(x) | x.__int__() | | 浮点数 | float(x) | x.__float__()| | 四舍五入整数 | round(x) | x.__round__() | |四舍五入至n位小数 | round(x, n) | x.__round__(n)| | >= x的最小整数 | math.ceil(x) | x.__ceil__() | | <= x的最大整数 | math.floor(x) | x.__floor__() | | 对x朝向0取整 | math.trunc(x) | x.__trunc__() | | 作为索引的数字 | a_list[x] | a_list[x.__index__()] |
可比较的类
许多数据类型都可以进行比较,可以使用以下特殊方法来实现。
目的 | 代码实现 | python实际调用 |
---|---|---|
相等 | x == y | x.__eq__(y) |
不相等 | x!= y | x.__ne__(y) |
小于 | x < y | x.__lt__(y) |
小于或等于 | x <= y | x.__le__(y) |
大于 | x>y | x.__gt__(y) |
大于或等于 | x >= y | x.__ge__(y) |
布尔上下文环境的真值 | if x : | x.__bool__() |
可序列化的类
python支持任意对象的可序列化和反序列化。
目的 | 代码 | python实际调用 |
---|---|---|
自定义对象的复制 | copy.copy(x) | x.__copy__() |
自定义对象的深度复制 | copy.deepcopy(x) | x.__deepcopy__() |
在pickling之前获取对象的状态 | pickle.dump(x, file) | x.__getstate__() |
序列化某对象 | pickle.dump(x, file) | x.__reduce__() |
序列化某对象(新pickling协议) | pickle.dump(x,file, protocol_version) | x.__reduce_ex__(protocol_verion) |
控制unpickling过程中对象的创建方式 | x = pickle.load(file) | x.__getnewargs__() |
在unpickling之后还原对象的状态 | x = pickle.load(file) | x.__setstate__() |
可在WITH语块中使用的类
with语块定义了运行时刻上下文环境;在执行with语句时将“进入”该上下文环境,而执行该语块中的最后一条语句将“退出"该上下文环境。
目的 | 代码 | python实际调用 |
---|---|---|
进入with语块时进行一些特别操作 | with x: | x.__enter__() |
在退出with语块时进行一些特别操作 | with x: | x.__exit__() |
以下是with file习惯用法的运作方式:
def _checkClosed(self, msg=None):
if self.closed:
raise ValueError('I/O operation on closed file.'
if msg is None else msg)
def __enter__(self):
self._checkClosed()
return self
def __exit__(self, *args):
self.close()
该文件对象同时定义了一个__enter__()和一个__exit__()方法。该__enter__()方法检查文件是否处于打开状态;如果没有,_checkClosed方法触发一个例外
__enter__方法将始终返回self,这是with语块将用于调用属性和方法的对象。
在with语块结束后,文件对象将自动关闭
其他
如果知道自己在做什么,几乎可以完全控制类是如何比较的、属性如何定义,以及类的子类是何种类型。
目的 | 代码 | python实际调用 |
---|---|---|
类构造器 | x=MyClass() | x.__new__() |
类析构器 | del x | x.__del__() |
只定义特定集合的某些特性 | x.__slots__() | |
自定义散列值 | hash(x) | x.__hash__() |
获取某个属性的值 | x.color | type(x).__dict__['color'].__get__(x,type(x)) |
设置某个属性的值 | x.color = 'PapayWhip' | type(x).__dict__['color'].__set__(x, 'PapayaWhip') |
删除某个属性 | del x.color | type(x).__dict__['color'].__del__(x) |
控制某个对象是否该对象的实例 | isinstance(x, MyClass) | MyClass.__instancecheck__(x) |
控制某个类是否是该类的子类 | issubclass(C,MyClass) | MyClass.__subclasscheck__(C) |
控制某个类是否是该抽象基类的子类 | issubclass(C, MyABC) | MyABC.__subclasshook__(C) |