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)

by 李鹏