Python 魔法方法
目录
1 简介
在 Python
中, 你可以通过定义一些特殊的方法来控制一个对象的行为。
这些方法的名字常用名为 魔法方法 或 魔术方法.
当然了, 官方文档的名字更加直接, 叫做 Special method.
这篇博客主要借鉴 Python 魔术方法指南 对 部分 魔法方法进行总结。
2 构造和初始化
Python
一个对象实例化到销毁调用的方法的顺序是:
__new__() ==> __init__() ==> ... ==> __del__()
因为最常用的方法是 __init__()
, 所以我一直认为最先调用的方法应该是 __init__()
,
但结果居然还有一个 __new__()
. 没人气的方法 @_@.
- __new__(cls[,…]))
- 对象实例化的时候所调用的第一个方法, 它的第一个参数是这个类,其他的参数是用来直接传递给
__init__()
方法。 - __init__(self[, …])
- 类的初始化方法。 可以说是最常用的方法了。 调用构造函数时的参数就是传给它的。
- __del__(self)
- 当实例即将被销毁时调用, 也被叫做析构函数。 没有参数.
class Test(object): def __init__(self): print("Hello World !") def __del__(self): print("Goodbye World QAQ") T = Test() del T
3 比较和数值运算
一个有趣的例子:
# one list >>> l = [0] # one tuple >>> t = (1, ) # list + tuple ? >>> l + t Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "tuple") to list # list += tuple ? >>> l += t [0, 1]
一个列表直接加一个元组会出错, 而 +=
没有问题…
当时我很疑惑, 有大佬给出了解答:
l + t
对应的是 l.__add__(t)
,而 l += t
对应的是 l.__iadd__(t)
,实现可以是不同的.
当然, 大佬还说了这样的代码学习可以,就不要在实际操作中用了。
3.1 用于比较的魔法方法
- __cmp__(self, other)
- 最基本的用于比较的魔法方法, 现了所有的比较符号 (<,==,!=,etc.). 如果
self < other
应该返回一个 负数, 如果self == other
应该返回 0, 如果self > other
应该返回一个 正数. - __eq__(self, other)
- 定义了
==
行为, 相等返回True
, 反之返回False
. - __ne__(self, other)
- 定义了
!=
行为, 不等返回True
, 反之返回False
. - __lt__(self, other)
- 定义了
<
行为, 小于返回True
, 反之返回False
. - __gt__(self, other)
- 定义了
>
行为, 大于返回True
, 反之返回False
.
class Word(str): '''存储单词的类,定义比较单词的几种方法''' def __new__(cls, word): # 注意我们必须要用到__new__方法,因为str是不可变类型 # 所以我们必须在创建的时候将它初始化 if ' ' in word: print "Value contains spaces. Truncating to first space." word = word[:word.index(' ')] #单词是第一个空格之前的所有字符 return str.__new__(cls, word) def __gt__(self, other): return len(self) > len(other) def __lt__(self, other): return len(self) < len(other) def __ge__(self, other): return len(self) >= len(other) def __le__(self, other): return len(self) <= len(other)
这里就直接可耻的盗用 指南 的例子了。
3.2 数值处理的魔法方法
- 一元操作符和函数
- __pos__(self)
- 实现正号的特性, 如
+object
- __neg__(self)
- 实现负号的特性, 如
-object
- __abs__(self)
- 实现内置
abs()
函数的特性, 如abs(object)
- __invert__(self)
- 实现
~
符号的特性, 参考文章: Bitwise operation
- 普通算数操作符
- __add__(self, other)
- 实现加法, 如
A + B
- __sub__(self, other)
- 实现减法, 如
A - B
- __mul__(self, other)
- 实现乘法, 如
A * B
- __floordiv__(self, other)
- 实现
//
除, 如A // B
- __div__(self, other)
- 实现
/
除, 如A / B
- __truediv__(self, other)
实现真除法, 需要:
from __future__ import division
- __mod__(self, other)
- 实现取模运算, 如
A % B
- __divmod___(self, other)
- 实现内置函数
divmod()
运算, 如divmod(obj)
- __pow___(self, other[, modulo])
- 实现使用
**
的指数运算, 如A**2
- __lshift__(self, other)
- 实现使用
<<
的按位左移运算, 如A << B
- __rshift__(self, other)
- 实现使用
>>
的按位右移运算, 如A >> B
- __and__(self, other)
- 实现使用
&
的按位与运算, 如A & B
- __or__(self, other)
- 实现使用
|
的按位或运算, 如A | B
- __xor__(self, other)
- 实现使用
^
的按位异或运算, 如A ^ B
反运算
指南给出的反运算的解释:
一个普通的加法运算: some_object + other 对应的反运算: other + some_object
意思就是把操作数调了个位置, 大多数情况下,反运算的结果是与普通运算相同的。
具体的实现和 普通算数操作符 的实现差不多, 只需要在对应的运算前面加上一个
r
就可以了。如:
__add__()
对应__radd__()
.Update 2018-05-14: 突然明白反运算是什么意思了:
- self + other 就是正运算
- other + self 就是反运算
增量赋值
这个就是比较熟悉的运算了, 就是类似于
A += B
的运算, 实现方式和 普通算数操作符 类似,只需要在对应的运算前面加上一个i
就可以了。如:
__add__()
对应__iadd__()
.实际操作就是对应的运算符紧跟一个等号
=
.如:
+=
,>>=
,**=
…
4 类型转化
可以通过一些 魔法方法 来实现内置类型类型转换, 注意, 是转换为 内置类型.
- __int__(self)
- 实现整形的强制转换
- __long__(self)
- 实现长整形的强制转换, 当然, 这应该是
Python2
的 - __float__(self)
- 实现浮点型的强制转换
- __complex__(self)
- 实现复数的强制转换
- __oct__(self)
- 实现八进制的强制转换
- __hex__(self)
- 实现二进制的强制转换
- __index__(self)
- 当对象是被应用在 切片表达式 中时,实现整形强制转换
- __trunc__(self)
- 当使用
math.trunc(self)
的时候被调用, 应该返回数值被截取成整形的值 - __coerce__(self, other)
- 实现混合模式算数, 也是
Python2
的
转换成什么类型就返回什么类型的值。
5 表现类
- __str__(self)
- 使用
str(obj)
会调用这个方法 - __repr__(self)
- 使用
repr(obj)
会调用这个方法 - __unicode(self)
- 使用
unicode(obj)
会调用这个方法, 当然这也是Python2
的 - __hash__(self)
- 使用
hash(obj)
会调用这个方法, 应该返回一个 整型 - __nonzero(self)
- 使用
bool(obj)
会调用这个方法, 应该返回True
或False
. 在Python3
中改为了__bool__().
6 属性访问控制
作为动态语言, Python
一个对象的属性的访问是很开放的。 如果需要对属性的访问进行控制,就可以使用这一部分的 魔法方法.
- __getattr__(self, name)
- 获取不存在的属性的值时会调用这个方法, 所以在这个方法内可以获取正常属性的值。
- __getattribute__(self, name)
获取对象的属性时调用, 不管属性存不存在。 同时如果定义了这个方法,
__getattr__
将不起作用, 除非显示调用或引发AttributeError
.由于获取任意属性都将调用这一方法, 所以如果在这个方法内获取属性将导致 无限递归.
解决这一问题的方法是把获取属性的方法指向一个更高的 超类:
class ClassA: def __getattribute__(self, item): return super().__getattribute__(self, item)
- __setattr__(self, name, value)
定义设置对象的属性时的行为, 即
obj.xxx = xxx
时会调用这个方法使用这个方法需要注意的一点:
def __setattr__(self, name, value): self.name = value # 每当属性被赋值的时候, ``__setattr__()`` 会被调用,这样就造成了递归调用 # 这意味这会调用 ``self.__setattr__('name', value)`` ,每次方法会调用自己。这样会造成程序崩溃 def __setattr__(self, name, value): # 通过这种方式来避免 self.__dict__[name] = value # 给类中的属性名分配值
设置 实例 属性会调用这个方法, 包括在
__init__
中设置属性。 但类属性不受影响。- __delattr__(self, name)
- 定义删除对象的属性时的行为, 即
del obj.xxx
时会调用这个方法
对于这一部分的 魔法方法, 每个 对象 的 __dict__
属性应该是一个很重要的内容, 有兴趣可以了解一下 ☞ object.__dict__.
7 创建定制的序列
Python
中的序列有 可变 和 不可变 的区分, 比如 tuple
和 list
.
通过魔法方法的定义来控制创建的序列的类型:
- 不可变: 只能定义
__len__
和__getitem__
. - 可变: 需要定义
__setitem__
和__delitem__
. - 可迭代: 需要定义
__iter__
返回一个迭代器
这些魔法方法的具体作用:
- __len__(self)
- 使用
len(obj)
的时候调用, 返回序列的长度 - __getitem__(self, key)
- 使用
obj[key]
时调用, 返回序列指定键的值 - __setitem__(self, key, value)
- 使用
obj[key] = value
时调用, 设置序列指定键的值 - __delitem__(self, key)
- 使用
del obj[key]
时调用, 删除序列指定条目 - __iter__(self)
- 返回迭代器,
iter(obj)
- __reversed__(self)
- 使用
reversed(obi)
时调用, 反转序列 - __contains__(self, item)
- 使用
item in obj
和item not in obj
时调用, 检测成员是否存在 - __concat__(self, other)
- 连接两个序列的时候调用
8 反射
通过魔法方法控制使用 isinstance
和 issubclass
的反射行为:
- __instancecheck__(self, instance)
- 检查一个实例是不是定义的类的实例, 对应
isinstance
- __subclasscheck__(self, subclass)
- 检查一个类是不是定义的类的子类, 对应
issubclass
9 可以调用的对象
如果你希望可以向调用函数那样调用你的 对象, 那么你可以定义那个对象的 __call__
方法。
- __call__(self, [args…])
- 使用
obj(...)
时调用
class Test(object): def __init__(self): self.val = 10 def __call__(self): return self.val t = Test() t()
10 上下文管理器
Python
中使用 with
可以写出更加简洁高效的代码, 而能够使用 with
的对象需要支持上下文管理器。
上下文管理器需要两个方法: __enter__
和 __exit__
.
with expression [as variable]: with-block
- __enter__(self)
- 这个方法在进入
with-block
之前调用, 返回值被with
的目标或as
后的名字绑定 - __exit__(self, exception_type, exception_value, traceback)
- 这个方法在退出
with-block
时调用
class Closer(object): '''通过with语句和一个close方法来关闭一个对象的会话管理器''' def __init__(self, obj): self.obj = obj def __enter__(self): return self.obj # bound to target def __exit__(self, exception_type, exception_val, trace): try: self.obj.close() except AttributeError: # obj isn't closable print 'Not closable.' return True # exception handled successfully
11 更多的方法
Python
的魔法方法还有不少, Python2
和 Python3
在这方面也有一定的区别。