本指南是数月博客的总结。主题是魔术方法。
什么是魔术方法呢?它们是面向对象Python语言中的一切。它们是你可以自定义并添加“魔法”到类中的特殊方法。它们被双下划线环绕(比如__init__或__lt__)。它们的文档也不像它所需要的那么齐备。Python的所有魔术方法都在Python文档的同一区域,但它们的使用分散,组织松散。而且文档的这部分区域中几乎没有一个示例(这很有可能是设计好的,因为在语法参考里它们都很详尽,但伴随的是枯燥的语法描述等等)。我们每个知道的最基本的“魔法”方法是__init__。一种让我们在初始化一个类时定义一些行为。然而当我执行 x = SomeClass(), __init__ 不是第一个被执行的。事实上,第一被执行的的方法是__new__,它会创建一个实例,然后在构造器创建时传递一些参数。在一个object的生命周期的另一端的方法是__del__。让我们仔细看看这3个“魔法”方法:
__new__ 是一个类的初始化过程中第一个被执行的方法。它创建了类,然后把一些参数传递给__init__。__new__ 很少被使用,特别是当我们用一些不可变类型的子类时(像tuple ,string),我不想关心__new__的太多的细节,因为那是没有用的。但它有它存在的意义。更多详细的请看in the Python docs.
__init__(self, [...)
类的构造器,当初始构造方法被执行(例如,我们执行 x = SomeClass(10,'foo')),__init__ 就会获得 10 和 ‘foo’ 作为参数。__init__ 在python类的定义中经常被使用
__del__(self)
若果 __new__ 和 __init__ 形成一个类的构造函数,__del__ 是就是析构函数。它不实现语句 del x 的行为(这样代码就不会转换为 x.__del__())。它定义了一个被垃圾回收的行为。它在类消除的时候需要做一些额外的行为时是非常有用的,就像 sockets 和 file 类。注意,当编译器还在运行,如果类还存活着,这里不能确保__del__一定会被执行。所以__del__ 不能替代一些良好的编程习惯(比如连接用完了将其关掉),事实上__del__很少被使用,因为它的调用是非常不稳定的;请谨慎使用!
把他们合起来后,这里就是一个 __init__ 和 __del__ 使用的例子:
from os.path import joinclass FileObject:
'''Wrapper for file objects to make sure the file gets closed on deletion.'''
def __init__(self, filepath='~', filename='sample.txt'):
# open a file filename in filepath in read and write mode
self.file = open(join(filepath, filename), 'r+')
def __del__(self):
self.file.close()
del self.file
我们使用Python的“魔法”方法最大得优势之一是它提供了一种简单的方法去定义类的行为,比如 built-in 类型。这就意味着你可以避免丑陋的,违反直觉的,非标准化的基本操作方法。在一些语言中,他们通常这样写:
if instance.equals(other_instance):
# do something
当让Python中也可以这么做,但是这增加了混乱和不必要的冗余。不同的类库中的相同的方法可能会用不同名字,使得使用者做了太多不必要的操作。相比之下“魔法”方法是强大的,我们可以使用它定义一个方法代替上面的例子(__eq__ , 在这个例子中):if instance == other_instance:
#do something
这是“魔法”方法强大用途的一部分。他们绝大部分让我们定义操作的意义,以至于我们可以使用他们在我们自己的类中就像使用built in 类型。Python有用于实现整个类之间的比较的“魔法方法”,这些方法设计的很直观,没有一点不方便调用。而且他们还提供重载Python默认的类之间的比较行为的方法。这里列出他们的列表和他们的功能:
__cmp__(self, other)class Word(str):
'''Class for words, defining comparison based on word length.'''
def __new__(cls, word):
# Note that we have to use __new__. This is because str is an immutable
# type, so we have to initialize it early (at creation)
if ' ' in word:
print "Value contains spaces. Truncating to first space."
word = word[:word.index(' ')] # Word is now all chars before first space
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)
现在,我们可以创建两个单词(通过使用Word('foo')和Word('bar'))然后依据长度比较它们。但要注意,我们没有定义__eq__和__ne__,因为这样会导致其他一些奇怪的行为(尤其是Word('foo')==Word('bar')会判定为true),它不是基于长度相等意义上的测量,所以我们回归到字符平等意义上的实现。就如同你可以通过定义比较操作来比较你自己的类实例一样,你也可以自己定义数学运算符号的行为。好吧,先系紧你的裤腰带,深呼吸......,这些操作可多着呢。由于文章组织需要,我把这些数学“魔术方法”分为5类:单目运算操作,一般数学运算操作,满足交换律的数学运算(后面会有更多介绍),参数赋值操作和类型转换操作:
单目运算符操作与函数:一般算数运算
反射算术运算符
你相信我说我能用一位来表示反射运算吗?可能有人会认为表示一个反射运算是大的吓人的“外国概念”,反射实际上它是非常简单的。看下面的例子:
some_object + other
这是一个正常的加法。除了可以交换操作数以外,反射运算和加法是一样的:
other + some_object
除了执行那种 other对像作为第一个操作数,而它自身作为第二个操作数的运算以外,所有的魔法方法做的事情与正常运算表示的意义是等价的。在大部分情况下反射运算结果和它正常的运算是等价的,所以你可以不定义__radd__,而是调用__add__等等。注意,对像(本例中的other)在运算符左边的时候,必须保证该对像没有定义(或者返回NotImplemented的)它的非反射运算符。例如,在这个例子中,some_object.__radd__ 只有在 other没有定义__add__的时候才会被调用。
__radd__(self, other)
反射加法
__rsub__(self, other)
反射减法的
__rmul__(self, other)
反射除法
__rfloordiv__(self, other)
反射地板除,使用//运算符的
__rdiv__(self, other)
反射除法,使用/运算符的.
__rtruediv__(self, other)
反射真除.注意只有from __future__ import division 的时候它才有效
__rmod__(self, other)
反射取模运算,使用%运算符.
__rdivmod__(self, other)
长除法,使用divmod()内置函数,当divmod(other,self)时被调用.
__rpow__
反射乘方,使用**运算符的
__rlshift__(self, other)
反射左移,使用<<操作符.
__rrshift__(self, other)
反射右移,使用>>操作符.
__rand__(self, other)
反射位与,使用&操作符.
__ror__(self, other)
反射位或,使用|操作符.
__rxor__(self, other)
反射异或,使用^操作符.
增量运算
Python 还有很多种魔法方法,允许一些习惯行为被定义成增量运算。你很可能已经熟悉了增量运算,增量运算是算术运算和赋值运算的结合。如果你还不知道我在说什么,就看一下下面的例子:
x = 5x += 1 # in other words x = x + 1
每一个方法的返回值都会被赋给左边的变量。(比如,对于a += b, __iadd__ 可能会返回a + b, a + b会赋给变量a。) 下面是清单:
__iadd__(self, other)
加法赋值
__isub__(self, other)
减法赋值.
__imul__(self, other)
乘法赋值
__ifloordiv__(self, other)
整除赋值,地板除,相当于 //= 运算符.
__idiv__(self, other)
除法赋值,相当于 /= 运算符.
__itruediv__(self, other)
真除赋值,注意只有你 whenfrom __future__ import divisionis,才有效.
__imod_(self, other)
模赋值,相当于 %= 运算符.
__ipow__
乘方赋值,相当于 **= 运算符.
__ilshift__(self, other)
左移赋值,相当于 <<= 运算符.
__irshift__(self, other)
左移赋值,相当于 >>= 运算符.
__iand__(self, other)
与赋值,相当于 &= 运算符.
__ior__(self, other)
或赋值,相当于 |= 运算符.
__ixor__(self, other)
异或运算符,相当于 ^= 运算符.
类型转换魔法
Python 同样有一系列的魔法方法旨在实现内置类型的转换,比如float() 函数。它们是:
__int__(self)
转换成整型.
__long__(self)
转换成长整型.
__float__(self)
转换成浮点型.
__complex__(self)
转换成 复数型.
__oct__(self)
转换成八进制.
__hex__(self)
转换成十六进制.
__index__(self)
当对象被切片时转换成int型。如果你定义了一个可能被用来做切片操作的数值型,你就应该定义__index__.
__trunc__(self)
当 math.trunc(self) 使用时被调用.__trunc__返回自身类型的整型截取 (通常是一个长整型).
__coerce__(self, other)
执行混合类型的运算,如果转换不能完成,应该返回None;否则,要返回一对两个元数的元组self和other, 被操作成同类型。
增量运算
Python 还有很多种魔法方法,允许一些习惯行为被定义成增量运算。你很可能已经熟悉了增量运算,增量运算是算术运算和赋值运算的结合。如果你还不知道我在说什么,就看一下下面的例子:
x = 5x += 1 # in other words x = x + 1类型转换魔法
Python 同样有一系列的魔法方法旨在实现内置类型的转换,比如float() 函数。它们是:
__int__(self)用一个字符串来表示一个类往往会非常有用。在Python中,有很多你可以在类定义中实施的方法来自定义内置函数的返回值以表示出你所写出的类的某些行为。
__format__(self, formatstr)
定义当你的一个类的实例被用来用新式的格式化字符串方法进行格式化时所要产生的行为。例如, "Hello, {0:abc}!".format(a) 将会导致调用 a.__format__("abc") 。这对定义你自己的数值或字符串类型是十分有意义的,你可能会给出一些特殊的格式化选项。__sizeof__(self)
定义当 sys.getsizeof() 被你的一个类的实例调用时所要产生的行为。该方法应该以字节为单位,返回你的对象的大小。这通常对于以C扩展的形式实现的Python类更加有意义,其有助于理解这些扩展。很多用过其它语言的人抱怨Python缺乏对类真正的封装(比如没办法定义private属性和public的getter和settter)。但这不是真的啊:真相是Python通过“魔法”实现了大量的封装,而不是使用明确的方法或字段修饰符。看一下吧:
__setattr__(self, name, value)
与__getattr__不同,__setattr__是一个真正的封装方案。它允许你定义当给一个存在或不存在的属性赋值时的行为,意味着对任何属性值的改变你都可以定义一个规则。可是,你得小心使用__setattr__,在这个清单结尾的例子会向你说明。__delattr__
它与__setattr__非常像, 只不过是用来删除而不是设置属性。 __detattr__需要预防措施,就像setattr一样,当被调用时可能会引起无限递归(当__delattr__已经实现时,调用 del self.name 就会引起无限的递归)。__getattribute__(self, name)
__getattribute__相当适合它的同伴__setattr__和__delattr__.但我却不建议你使用它。__getattribute__只有在新风格的类中才会被使用(所有的新风格类在Python最新的版本中),在老版本中,你可以子类化object来获得一个新风格类。它允许你定义一条规则来处理无论什么时候属性值被访问时的行为。比如类似于由于其它的伙伴犯错而引起的无限递归(这时你就可以调用基类的__getattribute__方法来阻止它)。它也避免了对__getattr__的依赖,当__getattribute__方法已经实现的时候,__getattr__只有在__getattribute__被明确的调用或抛出一个AttributeError异常的时候才会被调用。这个方法能被使用(毕竟,这是你的选择),但是我不推荐它,因为它很少使用并且运行的时候很难保证没有BUG。如果定义了任何属性访问控制方法,容易产生错误。思考下面这个例子:
def __setattr__(self, name, value):
self.name = value
# since every time an attribute is assigned, __setattr__() is called, this
# is recursion.
# so this really means self.__setattr__('name', value). Since the method
# keeps calling itself, the recursion goes on forever causing a crashdef __setattr__(self, name, value):
self.__dict__[name] = value # assigning to the dict of names in the class
# define custom behavior here
我们从Python中定制的属性访问中学到了什么?它们不是被轻易使用的。事实上,它有点过分强大并且违反直觉。但它们存在的原因是用来止痒的:Python不阻止你制造遭糕东西,但可能会让它变的困难。自由是最重要的东西,所以你可做任何你想做的事情。这里有一个例子,展示了一些特殊的属性访问控制行为。(注意我们使用super,因为不是所有的类都有__dict__属性):
class AccessCounter(object):
'''A class that contains a value and implements an access counter. The counter increments each time the value is changed.'''
def __init__(self, val):
super(AccessCounter, self).__setattr__('counter', 0)
super(AccessCounter, self).__setattr__('value', val)
def __setattr__(self, name, value):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
# Make this unconditional.
# If you want to prevent other attributes to be set, raise AttributeError(name)
super(AccessCounter, self).__setattr__(name, value)
def __delattr__(self, name):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
super(AccessCounter, self).__delattr__(name)]
有很多办法能让你的Python类使用起来就像内置的序列(dict,tuple,list,string等)。Python里有一些目前我最喜欢的办法,因为它们给你的控制到了荒谬的程度并且神奇地使得大量的全局函数优雅地工作在你类的实例当中。但是在深入讲这些好东西之前,我们先介绍下需求。
不需要再等待了,这里就是容器所使用的一些魔法方法。
__getitem__(self, key)
定义当某一项被访问时,使用self[key]所产生的行为。这也是不可变容器和可变容器协议的一部分。如果键的类型错误将产生TypeError;如果key没有合适的值则产生KeyError。__setitem__(self, key, value)
定义当一个条目被赋值时,使用self[nkey] = value所产生的行为。这也是协议的一部分。而且,在相应的情形下也会产生KeyError和TypeError。__delitem__(self, key)
定义当某一项被删除时所产生的行为。(例如del self[key])。这只是可变容器协议的一部分。当你使用一个无效的键时必须抛出适当的异常。__iter__(self)
返回一个容器迭代器,很多情况下会返回迭代器,尤其是当内置的iter()方法被调用的时候,以及当使用for x in container:方式循环的时候。迭代器是它们本身的对象,它们必须定义返回self的__iter__方法。__reversed__(self)
实现当reversed()被调用时的行为。应该返回序列反转后的版本。仅当序列可以是有序的时候实现它,例如对于列表或者元组。__contains__(self, item)
定义了调用in和not in来测试成员是否存在的时候所产生的行为。你可能会问为什么这个不是序列协议的一部分?因为当__contains__没有被定义的时候,Python会迭代这个序列,并且当找到需要的值时会返回True。__missing__(self, key)
其在dict的子类中被使用。它定义了当一个不存在字典中的键被访问时所产生的行为。(例如,如果我有一个字典d,当"george"不是字典中的key时,使用了d["george"],此时d.__missing__["george"]将会被调用)。class FunctionalList:
'''A class wrapping a list with some extra functional magic, like head, tail, init, last, drop, and take.'''
def __init__(self, values=None):
if values is None:
self.values = []
else:
self.values = values
def __len__(self):
return len(self.values)
def __getitem__(self, key):
# if key is of invalid type or value, the list values will raise the error
return self.values[key]
def __setitem__(self, key, value):
self.values[key] = value
def __delitem__(self, key):
del self.values[key]
def __iter__(self):
return iter(self.values)
def __reversed__(self):
return FunctionalList(reversed(self.values))
def append(self, value):
self.values.append(value)
def head(self):
# get the first element
return self.values[0]
def tail(self):
# get all elements after the first
return self.values[1:]
def init(self):
# get elements up to the last
return self.values[:-1]
def last(self):
# get last element
return self.values[-1]
def drop(self, n):
# get all elements except first n
return self.values[n:]
def take(self, n):
# get first n elements
return self.values[:n]
你也可以控制怎么使用内置函数sisinstance()和issubclass()方法反射定义魔法方法.
这个魔法方法是:
__instancecheck__(self, instance)检查对象是否是您定义的类的一个实例(例.isinstance(instance, class)).
__subclasscheck__(self, subclass)检查类是否是你定义类的子类 (例.issubclass(subclass, class)).
这些魔法方法的用例看起来很小, 并且确实非常实用. 我不想花太多时间在反射魔法方法上,因为它们不是非常重要, 但是它们反应了关于面向对象程序上一些重要的东西在Python上,并且总的来说Python: 总是一个简单的方法去找某些事情, 即使是没有必要的. 这些魔法方法可能看起来不是很有用, 但是一旦你需要它们,你会感到庆幸它们的存在 (并且为自己阅读了本指南高兴!).
http://docs.python.org/2/library/abc.html.
你也许已经知道,在Python中,方法是最高级的对象。这意味着他们也可以被传递到方法中,就像其他对象一样。这是一个非常惊人的特性。
在Python中,一个特殊的魔法方法可以让类的实例的行为表现的像函数一样,你可以调用它们,将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性,其让Python编程更加舒适甜美。__call__(self, [args...])
允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。注意 __call__ 的参数可变。这意味着你可以定义 __call__ 为其他你想要的函数,无论有多少个参数。class Entity:
'''表示一个实体的类。调用该类以更新实体的位置。'''
def __init__(self, size, x, y):
self.x, self.y = x, y
self.size = size
def __call__(self, x, y):
'''Change the position of the entity.'''
self.x, self.y = x, y
# snip...
with open('foo.txt') as bar:
# 执行一些针对bar的操作
__enter__(self)
定义了当使用with语句的时候,会话管理器在块被初始创建时要产生的行为。请注意,__enter__的返回值与with语句的目标或者as后的名字绑定。__exit__(self, exception_type, exception_value, traceback)
定义了当一个代码块被执行或者终止后,会话管理器应该做什么。它可以被用来处理异常、执行清理工作或做一些代码块执行完毕之后的日常工作。如果代码块执行成功,exception_type,exception_value,和traceback将会为None。否则,你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,请确保__exit__在所有语句结束之后返回True。如果你想让异常被会话管理器处理的话,那么就让其产生该异常。__enter__和__exit__对于那些定义良好以及有普通的启动和清理行为的类是很有意义的。你也可以使用这些方法来创建一般的可以包装其它对象的会话管理器。下面是一个例子:
class Closer:
'''通过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
>>> from magicmethods import Closer
>>> from ftplib import FTP
>>> with Closer(FTP('ftp.somesite.com')) as conn:
... conn.dir()
...
>>> conn.dir()
>>> with Closer(int(5)) as i:
... i += 1
...
Not closable.
>>> i
6
为了成为一个描述器,一个类必须至少有__get__,__set__,__delete__方法被实现,让我们看看这些魔法方法:
__set__(self, instance, value)
定义了当描述器的值被改变的时候的行为。instance是拥有该描述器类的一个实例。value是要设置的值。下面是一个描述器的实例:单位转换。
class Meter(object):
'''对于”米“的描述器。'''
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Foot(object):
'''对于”英尺“的描述器。'''
def __get__(self, instance, owner):
return instance.meter * 3.2808
def __set__(self, instance, value):
instance.meter = float(value) / 3.2808
class Distance(object):
'''用米和英寸来表示两个描述器之间的距离。'''
meter = Meter()
foot = Foot()
__copy__(self)
定义了当对你的类的实例调用copy.copy()时所产生的行为。copy.copy()返回了你的对象的一个浅拷贝——这意味着,当实例本身是一个新实例时,它的所有数据都被引用了——例如,当一个对象本身被复制了,它的数据仍然是被引用的(因此,对于浅拷贝中数据的更改仍然可能导致数据在原始对象的中的改变)。__deepcopy__(self, memodict={})
定义了当对你的类的实例调用copy.deepcopy()时所产生的行为。copy.deepcopy()返回了你的对象的一个深拷贝——对象和其数据都被拷贝了。memodict是对之前被拷贝的对象的一个缓存——这优化了拷贝过程并且阻止了对递归数据结构拷贝时的无限递归。当你想要进行对一个单独的属性进行深拷贝时,调用copy.deepcopy(),并以memodict为第一个参数。(译者注:pickle是用来直接保存Python对象的模块,在英文中有“腌制”的意思)
让我们深入挖掘pickling方法。假设你想保存一个字典并在之后检索它:你可以把它写入一个文件中,小心确保其有正确的语法,之后用exec()或者读取文件来检索它。但这很有可能是相当危险的:如果你将重要数据保存在纯文本中,它可能会损坏或者发生各种各样的改变,有些会让你的程序崩溃,有些甚至会在你的电脑上运行恶意代码。因此,我们应该使用 pickle方法:
import pickle
data = {'foo': [1, 2, 3],
'bar': ('Hello', 'world!'),
'baz': True}
jar = open('data.pkl', 'wb')
pickle.dump(data, jar) # write the pickled data to the file jar
jar.close()
几个小时之后,我们希望找回这些数据,现在我们只需unpickle它:
import pickle
pkl_file = open('data.pkl', 'rb') # connect to the pickled data
data = pickle.load(pkl_file) # load it into a variable
print data
pkl_file.close()
现在,我们要注意一点:pickle并不完美。被pickle序列化的文件很容易被意外或是有意损坏。pickle模块可能比一般的纯文本文件要来的安全,但它仍然可能会被利用去运行恶意代码。而且它在各个Python版本之间是不兼容的,所以不要传送pkl文件并妄想其他人可以打开它。但是,pickle确实是处理缓存和其他序列化任务的强有力工具。
__getinitargs__(self)
如果你想在你的类被unpickle的时候执行__init__方法,你可以重载__getinitargs__方法,它会返回一个元组,包含你想传给__init__方法的参数。注意,这种方法只适用于旧式的Python类型(译者注:区别于2.2中引入的新式类)。__getnewargs__(self)
对于新式类,在unpickle的时候你可以决定传给__new__方法的参数。以上方法可以返回一个包含你想传给__new__方法的参数元组。__getstate__(self)
除了储存__dict__中的原来的那些变量,你可以自定义使用pickle序列化对象时想要储存的额外属性。这些属性将在你unpickle文件时被__setstate__方法使用。__setstate__(self, state)
当文件被unpickle时,其中保存的对象属性不会直接被写入对象的__dict中,而是会被传入这个方法。这个方法和__getstate__是配套的:当他们都被定义了的时候,你可以任意定义对象被序列化存储时的状态。__reduce__(self)
当你定义扩展类(使用C语言实现的Python扩展类)时,可以通过实现__reduce__函数来控制pickle的数据。如果__reduce__()方法被定义了,在一个对象被pickle时它将被调用。如果它返回一个字符串,那么pickle在将在全局空间中搜索对应名字的对象进行pickle;它还可以返回一个元组,包含2-5个元素: 一个可以用来重建该对象的可调用对象,一个包含有传给该可调用对象参数的元组,传给__setstate__方法的参数(可选),一个用于待pickle对象列表的迭代器(译者注:这些对象会被append到原来对象的后面)(可选)调用对象,一个包含有传给该可调用对象参数的元组,传给__setstate__方法的参数(可选),一个用于待pickle对象列表的迭代器(译者注:这些对象会被append到原来对象的后面)(可选),一个用于待pickle的字典的迭代器(可选)。__reduce_ex__(self)
__reduce_ex__是为兼容性而设计的。如果它被实现了,__reduce_ex__将会取代__reduce__在pickle时被执行。__reduce__可以同时被实现以支持那些不支持__reduce_ex__的老版本pickling API。一个例子
我们以Slate为例,这一段记录一个值以及这个值是何时被写入的程序,但是,这个Slate有一点特殊的地方,就是当前值不会被保存。import time
class Slate:
'''Class to store a string and a changelog, and forget its value when pickled.'''
def __init__(self, value):
self.value = value
self.last_change = time.asctime()
self.history = {}
def change(self, new_value):
# Change the value. Commit last value to history
self.history[self.last_change] = self.value
self.value = new_value
self.last_change = time.asctime()
def print_changes(self):
print 'Changelog for Slate object:'
for k, v in self.history.items():
print '%s\t %s' % (k, v)
def __getstate__(self):
# Deliberately do not return self.value or self.last_change.
# We want to have a "blank slate" when we unpickle.
return self.history
def __setstate__(self, state):
# Make self.history = state and last_change and value undefined
self.history = state
self.value, self.last_change = None, None
附录 1: 如何调用Magic Method
一些magic method已经映射到自带的方法(built-in functions);这种情况下如何调用他们是显而易见的。然而,在其他情况下,调用它们就不那么容易了。本附录致力于展示能够调用magic method的一些不被察觉的语法。
Magic Method | 何时被调用(例子) | Explanation |
---|
__new__(cls [,...]) | instance = MyClass(arg1, arg2) | __new__ is called on instance creation |
__init__(self [,...]) | instance = MyClass(arg1, arg2) | __init__ is called on instance creation |
__cmp__(self, other) | self == other, self > other, etc. | Called for any comparison |
__pos__(self) | +self | Unary plus sign |
__neg__(self) | -self | Unary minus sign |
__invert__(self) | ~self | Bitwise inversion |
__index__(self) | x[self] | Conversion when object is used as index |
__nonzero__(self) | bool(self) | Boolean value of the object |
__getattr__(self, name) | self.name # name doesn't exist | Accessing nonexistent attribute |
__setattr__(self, name, val) | self.name = val | Assigning to an attribute |
__delattr__(self, name) | del self.name | Deleting an attribute |
__getattribute__(self, name) | self.name | Accessing any attribute |
__getitem__(self, key) | self[key] | Accessing an item using an index |
__setitem__(self, key, val) | self[key] = val | Assigning to an item using an index |
__delitem__(self, key) | del self[key] | Deleting an item using an index |
__iter__(self) | for x in self | Iteration |
__contains__(self, value) | value in self,value not in self | Membership tests using in |
__call__(self [,...]) | self(args) | "Calling" an instance |
__enter__(self) | with self as x: | with statement context managers |
__exit__(self, exc, val, trace) | with self as x: | with statement context managers |
__getstate__(self) | pickle.dump(pkl_file, self) | Pickling |
__setstate__(self) | data = pickle.load(pkl_file) | Pickling |
希望这个表能够解决你可能会遇到的哪个语法调用哪个magic method的问题。
这里我们列举出一些Python 3与2.x在对象模型上主要的的不同之处。
因为Python 3中string和unicode直接已经没有差别,__unicode__已经不存在了,并且__bytes__(它的行为与__str__和__unicode__类似)成为新的自带方法来构造byte数组。