====================
== Hi, I'm Vimiix ==
====================
Practice makes perfect (ง •̀_•́)ง

[转]Python中的魔术方法汇总

magic method Python

这是一篇很不错的总结文章,简单易理解,洒家忍不住要转载收藏一下。

源文链接:http://algo.site/?cat=60

基础:

如果你想…所以,你写…Python 调用…
初始化一个实例x = MyClass()x.__init__()
作为一个字符串的"官方"表示repr(x)x.__repr__()
作为一个字符串str(x)x.__str__()
作为字节数组bytes(x)x.__bytes__()
作为格式化字符串format(x, format_spec)x.__format__(format_spec)
  • __init__()方法在创建实例后调用.如果你想控制创建过程,请使用__new__()方法
  • 按照惯例, __repr__() 应该返回一个有效的 Python 表达式的字符串
  • __str__()方法也被称为你的print(x)

迭代相关

如果你想…所以,你写…Python 调用…
遍历一个序列iter(seq)seq.__iter__()
从迭代器中获取下一个值next(seq)seq.__next__()
以相反的顺序创建一个迭代器reversed(seq)seq.__reversed__()
  • __iter__()无论何时创建新的迭代器,都会调用该方法.
  • __next__()每当你从迭代器中检索一下个值的时候,都会调用该方法
  • __reversed__()方法并不常见.它需要一个现有序列并返回一个迭代器,该序列是倒序的顺序.

属性

如果你想…所以,你写…Python 调用…
得到一个属性x.my_propertyx.__getattribute__('my_property')
获得一个属性x.my_propertyx.__getattr__('my_property')
设置一个属性x.my_property = valuex.__setattr__('my_property', value)
阐述一个属性del x.my_propertyx.__delattr__('my_property')
列出所有属性和方法dir(x)x.__dir__()
  • 如果你的类定义了一个__getattribute__()方法,Python 将在每次引用任何属性或方法名时调用它.
  • 如果你的类定义了一个__getattr__()方法,Python 只会在所有普通地方查找属性后调用它.如果一个实例x定义了一个属性 color, x.color将不会调用x.__getattr__('color'); 它将简单地返回已经定义的x.color值.
  • __setattr__()只要你为属性指定值,就会调用该方法.
  • __delattr__()只要删除属性,就会调用该方法.
  • __dir__()如果您定义一个__getattr__() 或者 __getattribute__() 方法,该方法很有用.通常情况下,调用dir(x)只会列出常规属性和方法.

**getattr()和getattribute()方法之间的区别很微妙但很重要.**

函数类

通过定义call()方法,您可以创建一个可调用类的实例 – 就像函数可调用一样.

如果你想…所以,你写…Python 调用…
来"调用"像函数一样的实例my_instance()my_instance.__call__()

行为

如果你的类作为一组值的容器 – 也就是说,如果问你的类是否"包含"一个值是有意义的 – 那么它应该定义下面的特殊方法,使它像一个集合一样.

如果你想…所以,你写…Python 调用…
序列的数量len(s)s.__len__()
否包含特定的值x in ss.__contains__(s)

字典(映射)

如果你想…所以,你写…Python 调用…
通过它的 key 来获得值x[key]x.__getitem__(key)
通过它的 key 来设置一个值x[key] = valuex.__setitem__(key, value)
删除键值对del x[key]x.__delitem__(key)
为丢失的 key 提供默认值x[nonexistent_key]x.__missing__(nonexistent_key)

数字

如果你想…所以,你写…Python 调用…
x + yx.__add__(y)
x - yx.__sub__(y)
x * yx.__mul__(y)
整除x / yx.__trueiv__(y)
x // yx.__floordiv__(v)
取余x % yx.__mod__(y)
整除与取余divmod(x, y)x.__divmod__(y)
平方x ** yx.__pow__(y)
左移x << yx.__lshift__(y)
友移x >> yx.__rshift__(y)
按位 and 运算x & yx.__and__(y)
按位 xor 或运算x ^ yx.__xor__(y)
按位 or 运算`xy`

上述一组特殊方法采用第一种方法:给定x / y,它们提供了一种方法让x说"我知道如何用y整除自己".以下一组特殊方法解决了第二种方法:它们为 y 提供了一种方法来说"我知道如何成为分母,并将自己整除 x".

如果你想…所以,你写…Python 调用…
x + yx.__radd__(y)
x - yx.__rsub__(y)
x * yx.__rmul__(y)
整除x / yx.__rtrueiv__(y)
x // yx.__rfloordiv__(v)
取余x % yx.__rmod__(y)
整除与取余divmod(x, y)x.__rdivmod__(y)
平方x ** yx.__rpow__(y)
左移x << yx.__rlshift__(y)
友移x >> yx.__rrshift__(y)
按位 and 运算x & yx.__rand__(y)
按位 xor 或运算x ^ yx.__rxor__(y)
按位 or 运算`xy`

可是等等!还有更多!如果你正在进行"就地"操作,如x /= 3则可以定义更多特殊的方法.

如果你想…所以,你写…Python 调用…
x + yx.__iadd__(y)
x - yx.__isub__(y)
x * yx.__imul__(y)
整除x / yx.__itrueiv__(y)
x // yx.__ifloordiv__(v)
取余x % yx.__imod__(y)
整除与取余divmod(x, y)x.__idivmod__(y)
平方x ** yx.__ipow__(y)
左移x << yx.__ilshift__(y)
友移x >> yx.__irshift__(y)
按位 and 运算x & yx.__iand__(y)
按位 xor 或运算x ^ yx.__ixor__(y)
按位 or 运算`xy`

还有一些"单个数"数学运算可以让你自己对类似数字的对象进行数学运算.

如果你想…所以,你写…Python 调用…
负数-xx.__neg__()
正数+xx.__pos__()
绝对值abs(x)x.__abs__()
~xx.__invert__()
复数complex(x)x.__complex__()
整数int(x)x.__int__()
浮点数float(x)x.__float__()
四舍五入到最近的整数round(x)x.__round__()
四舍五入到最近的 n 位数round(x, n)x.__round__(n)
最小整数math.ceil(x)x.__ceil__()
最大整数math.floor(x)x.__floor__()
截断 x 到 0 的最接近的整数math.trunc(x)x.__trunc__()
数字作为列表索引a_list[x]a_list[x.__index__()]

比较

如果你想…所以,你写…Python 调用…
等于x == yx.__eq__(y)
不等于x != yx.__ne__(y)
小于x < yx.__lt__(y)
小于等于x <= yx.__le__(y)
大于x > yx.__gt__(y)
大于等于x >= yx.__ge__(y)
布尔if x:x.__bool__()

序列化

如果你想…所以,你写…Python 调用…
对象副本copy.copy(x)x.__copy__()
深拷贝copy.deepcopy(x)x.__deepcopy__()
序列化一个对象pickle.dump(x, file)x.__getstate__()
序列化一个对象pickle.dump(x, file)x.__reduce__()
序列化一个对象pickle.dump(x, file, protocol_version)x.__reduce_ex__(protocol_version)
取出恢复后的状态x = pickle.load(fp)x.__getnewargs__()
取出恢复后的状态x = pickle.load(fp)x.__setstate__()

with 语句

with 块限定了运行时上下文;在执行 with 语句时,“进入"上下文,并在执行块中的最后一个语句后"退出"上下文.

如果你想…所以,你写…Python 调用…
进入 with 语句块with x:x.__enter__()
退出 with 语句块with x:x.__exit__(exc_type, exc_value, traceback)

真正深奥的东西

如果你想…所以,你写…Python 调用…
一个类的构造函数x = MyClass()x.__new__()
一个类的析构函数del xx.__del__()
只有一组特定的属性需要定义x.__solts__()
hash 码hash(x)x.__hash__()
获得一个属性的值x.colortype(x).__dict__['color'].__get__(x, type(x))
设置一个属性的值x.color = 'PapayaWhip'type(x).__dict__['color'].__set__(x, 'PapayaWhip')
删除一个属性del x.colortype(x).__dict__['color'].__del__(x)
一个对象是否是你的一个类的实例isinstance(x, MyClass)MyClass.__instancecheck__(x)
一个类是否是你的类的子类isinstance(C, MyClass)MyClass.__subclasscheck__(C)
一个类是否是抽象基类的实例isinstance(C, MyABC)MyABC.__subclasshook__(C)

Python 正确调用__del__()特殊方法时非常复杂.为了完全理解它,你需要知道 Python 如何跟踪内存中的对象.这里有一篇关于Python 垃圾收集和类析构函数的好文章. (原文在墙外是一篇早期的英文文章,洒家顺手也给翻译了,请看下一篇文章)你还应该阅读关于弱引用,weakref 模块,以及可能的gc 模块以获得更好的度量.