Python 魔术方法

请注意,本文编写于 2370 天前,最后修改于 2340 天前,其中某些信息可能已经过时。

日常记录
原文地址:介绍Python的魔术方法 - Magic Method


__init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数".

实际上, 当我们调用x = SomeClass()的时候调用,__init__并不是第一个执行的, __new__才是。所以准确来说,是__new__和__init__共同构成了"构造函数".

__new__是用来创建类并返回这个类的实例, 而__init__只是将传入的参数来初始化该实例.

__new__在创建一个实例的过程中必定会被调用,但__init__就不一定,比如通过pickle.load的方式反序列化一个实例时就不会调用__init__。

__new__方法总是需要返回该类的一个实例,而__init__不能返回除了None的任何值。


<!--more-->


----------
__getattr__(self, name)
该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。

__setattr__(self, name, value)
__setattr__ 是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。
不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现__setattr__时要避免"无限递归"的错误,下面的代码示例中会提到。

__delattr__(self, name)
__delattr__与__setattr__很像,只是它定义的是你删除属性时的行为。实现__delattr__是同时要避免"无限递归"的错误。

__getattribute__(self, name)
__getattribute__定义了你的属性被访问时的行为,相比较,__getattr__只有该属性不存在时才会起作用。
因此,在支持__getattribute__的Python版本,调用__getattr__前必定会调用 __getattribute__。__getattribute__同样要避免"无限递归"的错误。
需要提醒的是,最好不要尝试去实现__getattribute__,因为很少见到这种做法,而且很容易出bug。


----------
__get__(self, instance, owner)
参数instance是拥有者类的实例。参数owner是拥有者类本身。__get__在其拥有者对其读值的时候调用。

__set__(self, instance, value)
__set__在其拥有者对其进行修改值的时候调用。

__delete__(self, instance)
__delete__在其拥有者对其进行删除的时候调用。


----------

__len__(self)
需要返回数值类型,以表示容器的长度。该方法在可变容器和不可变容器中必须实现。

__getitem__(self, key)
当你执行self[key]的时候,调用的就是该方法。该方法在可变容器和不可变容器中也都必须实现。
调用的时候,如果key的类型错误,该方法应该抛出TypeError;
如果没法返回key对应的数值时,该方法应该抛出ValueError。

__setitem__(self, key, value)
当你执行self[key] = value时,调用的是该方法。

__delitem__(self, key)
当你执行del self[key]的时候,调用的是该方法。

__iter__(self)
该方法需要返回一个迭代器(iterator)。当你执行for x in container: 或者使用iter(container)时,该方法被调用。

__reversed__(self)
如果想要该数据结构被內建函数reversed()支持,就还需要实现该方法。

__contains__(self, item)
如果定义了该方法,那么在执行item in container 或者 item not in container时该方法就会被调用。
如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。

__missing__(self, key)
dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。
比如d = {'a': 1}, 当你执行d[notexist]时,d.__missing__['notexist']就会被调用。


----------
__enter__(self)
__enter__会返回一个值,并赋值给as关键词之后的变量。在这里,你可以定义代码段开始的一些操作。

__exit__(self, exception_type, exception_value, traceback)
__exit__定义了代码段结束后的一些操作,可以这里执行一些清除操作,或者做一些代码段结束后需要立即执行的命令,比如文件的关闭,socket断开等。如果代码段成功结束,那么exception_type, exception_value, traceback 三个参数传进来时都将为None。如果代码段抛出异常,那么传进来的三个参数将分别为: 异常的类型,异常的值,异常的追踪栈。
如果__exit__返回True, 那么with声明下的代码段的一切异常将会被屏蔽。
如果__exit__返回None, 那么如果有异常,异常将正常抛出,这时候with的作用将不会显现出来。


----------
__getinitargs__(self)
如果你希望unpickle时,__init__方法能够调用,那么就需要定义__getinitargs__, 该方法需要返回一系列参数的元组,这些参数就是传给__init__的参数。

该方法只对old-style class有效。所谓old-style class,指的是不继承自任何对象的类,往往定义时这样表示: class A:, 而非class A(object):

__getnewargs__(self)
跟__getinitargs__很类似,只不过返回的参数元组将传值给__new__

__getstate__(self)
在调用pickle.dump时,默认是对象的__dict__属性被存储,如果你要修改这种行为,可以在__getstate__方法中返回一个state。state将在调用pickle.load时传值给__setstate__

__setstate__(self, state)
一般来说,定义了__getstate__,就需要相应地定义__setstate__来对__getstate__返回的state进行处理。

__reduce__(self)
如果pickle的数据包含了自定义的扩展类(比如使用C语言实现的Python扩展类)时,就需要通过实现__reduce__方法来控制行为了。由于使用过于生僻,这里就不展开继续讲解了。

令人容易混淆的是,我们知道, reduce()是Python的一个內建函数, 需要指出__reduce__并非定义了reduce()的行为,二者没有关系。

__reduce_ex__(self)
__reduce_ex__ 是为了兼容性而存在的, 如果定义了__reduce_ex__, 它将代替__reduce__ 执行。


----------
比较运算符
__cmp__(self, other)
如果该方法返回负数,说明self < other; 返回正数,说明self > other; 返回0说明self == other。
强烈不推荐来定义__cmp__, 取而代之, 最好分别定义__lt__等方法从而实现比较功能。
__cmp__在Python3中被废弃了。

__eq__(self, other)
定义了比较操作符==的行为.

__ne__(self, other)
定义了比较操作符!=的行为.

__lt__(self, other)
定义了比较操作符<的行为.

__gt__(self, other)
定义了比较操作符>的行为.

__le__(self, other)
定义了比较操作符<=的行为.

__ge__(self, other)
定义了比较操作符>=的行为.

下面我们定义一种类型Word, 它会使用单词的长度来进行大小的比较, 而不是采用str的比较方式。
但是为了避免 Word('bar') == Word('foo') 这种违背直觉的情况出现,并没有定义__eq__, 因此Word会使用它的父类(str)中的__eq__来进行比较。


----------

一元运算符和函数
__pos__(self)
实现了'+'号一元运算符(比如+some_object)

__neg__(self)
实现了'-'号一元运算符(比如-some_object)

__invert__(self)
实现了~号一元运算符(比如~some_object)

__abs__(self)
实现了abs()內建函数.

__round__(self, n)
实现了round()内建函数. 参数n表示四舍五进的精度.

__floor__(self)
实现了math.round(), 向下取整.

__ceil__(self)
实现了math.ceil(), 向上取整.

__trunc__(self)
实现了math.trunc(), 向0取整.

算术运算符
__add__(self, other)
实现了加号运算.

__sub__(self, other)
实现了减号运算.

__mul__(self, other)
实现了乘法运算.

__floordiv__(self, other)
实现了//运算符.

__div__(self, other)
实现了/运算符. 该方法在Python3中废弃. 原因是Python3中,division默认就是true division.

__truediv__(self, other)
实现了true division. 只有你声明了from __future__ import division该方法才会生效.

__mod__(self, other)
实现了%运算符, 取余运算.

__divmod__(self, other)
实现了divmod()內建函数.

__pow__(self, other)
实现了**操作. N次方操作.

__lshift__(self, other)
实现了位操作<<.

__rshift__(self, other)
实现了位操作>>.

__and__(self, other)
实现了位操作&.

__or__(self, other)
实现了位操作|

__xor__(self, other)
实现了位操作^


----------
类型转化
__int__(self)
实现了类型转化为int的行为.

__long__(self)
实现了类型转化为long的行为.

__float__(self)
实现了类型转化为float的行为.

__complex__(self)
实现了类型转化为complex(复数, 也即1+2j这样的虚数)的行为.

__oct__(self)
实现了类型转化为八进制数的行为.

__hex__(self)
实现了类型转化为十六进制数的行为.

__index__(self)


----------
__str__(self)
对实例使用str()时调用。

__repr__(self)
对实例使用repr()时调用。str()和repr()都是返回一个代表该实例的字符串,
主要区别在于: str()的返回值要方便人来看,而repr()的返回值要方便计算机看。

__unicode__(self)
对实例使用unicode()时调用。unicode()与str()的区别在于: 前者返回值是unicode, 后者返回值是str。unicode和str都是basestring的子类。

当你对一个类只定义了__str__但没定义__unicode__时,__unicode__会根据__str__的返回值自动实现,即return unicode(self.__str__());
但返回来则不成立。


----------
__format__(self, formatstr)
"Hello, {0:abc}".format(a)等价于format(a, "abc"), 等价于a.__format__("abc")。

这在需要格式化展示对象的时候非常有用,比如格式化时间对象。

__hash__(self)
对实例使用hash()时调用, 返回值是数值类型。

__nonzero__(self)
对实例使用bool()时调用, 返回True或者False。
你可能会问, 为什么不是命名为__bool__? 我也不知道。
我只知道该方法在Python3中改名为__bool__了。

__dir__(self)
对实例使用dir()时调用。通常实现该方法是没必要的。

__sizeof__(self)
对实例使用sys.getsizeof()时调用。返回对象的大小,单位是bytes。

__instancecheck__(self, instance)
对实例调用isinstance(instance, class)时调用。 返回值是布尔值。它会判断instance是否是该类的实例。

__subclasscheck__(self, subclass)
对实例使用issubclass(subclass, class)时调用。返回值是布尔值。它会判断subclass否是该类的子类。

__copy__(self)
对实例使用copy.copy()时调用。返回"浅复制"的对象。

__deepcopy__(self, memodict={})
对实例使用copy.deepcopy()时调用。返回"深复制"的对象。

添加新评论

评论列表