51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Python 魔术方法

日常记录
原文地址:介绍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__前必定会调用 getattributegetattribute__同样要避免"无限递归"的错误。 需要提醒的是,最好不要尝试去实现__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()时调用。返回"深复制"的对象。

赞(5)
未经允许不得转载:工具盒子 » Python 魔术方法