Python魔术方法指南入门构造和初始化构造定制类用于比较的魔术方法用于数值处理的魔术方法表现你的类控制属性访问创建定制序列反射可以调用的对象会话管理器创建描述器对象持久化对象总结附录介绍此教程为我的数篇文章中的一个重点。主题是魔术方法。什么是魔术方法?他们是面向对象的Python的一切。他们是可以给你的类增加”magic”的特殊方法。他们总是被双下划线所包围(e.g.__init__或者__lt__)。然而他们的文档却远没有提供应该有的内容。Python中所有的魔术方法均在Python官方文档中有相应描述,但是对于他们的描述比较混乱而且组织比较松散。很难找到有一个例子(也许他们原本打算的很好,在开始语言参考中有描述很详细,然而随之而来的确是枯燥的语法描述等等)。所以,为了修补我认为Python文档应该修补的瑕疵,我决定给Python中的魔术方法提供一些用平淡的语言和实例驱使的文档。我在开始已经写了数篇博文,现在在这篇文章中对他们进行总结。我希望你能够喜欢这篇文章。你可以将之当做一个教程,一个补习资料,或者一个参考。本文章的目的仅仅是为Python中的魔术方法提供一个友好的教程。构造和初始化每个人都知道一个最基本的魔术方法,__init__。通过此方法我们可以定义一个对象的初始操作。然而,当我调用x=SomeClass()的时候,__init__并不是第一个被调用的方法。实际上,还有一个叫做__new__的方法,来构造这个实例。然后给在开始创建时候的初始化函数来传递参数。在对象生命周期的另一端,也有一个__del__方法。我们现在来近距离的看一看这三个方法:__new__(cls,[...)__new__是在一个对象实例化的时候所调用的第一个方法。它的第一个参数是这个类,其他的参数是用来直接传递给__init__方法。__new__方法相当不常用,但是它有自己的特性,特别是当继承一个不可变的类型比如一个tuple或者string。我不希望在__new__上有太多细节,因为并不是很有用处,但是在Python文档中有详细的阐述。__init__(self,[…)此方法为类的初始化方法。当构造函数被调用的时候的任何参数都将会传给它。(比如如果我们调用x=SomeClass(10,'foo')),那么__init__将会得到两个参数10和foo。__init__在Python的类定义中被广泛用到。__del__(self)如果__new__和__init__是对象的构造器的话,那么__del__就是析构器。它不实现语句delx(所以代码将不会翻译为x.__del__())。它定义的是当一个对象进行垃圾回收时候的行为。当一个对象在删除的时候需要更多的清洁工作的时候此方法会很有用,比如套接字对象或者是文件对象。注意,因为当解释器退出的时候如果对象还存在,不能保证__del__能够被执行,所以__del__can’tserveasareplacementforgoodcodingpractices()~~~~~~~放在一起的话,这里是一个__init__和__del__实际使用的例子。fromos.pathimportjoinclassFileObject:'''给文件对象进行包装从而确认在删除时文件流关闭'''def__init__(self,filepath='~',filename='sample.txt'):#读写模式打开一个文件self.file=open(join(filepath,filename),'r+')def__del__(self):self.file.close()delself.file让定制的类工作起来使用Python的魔术方法的最大优势在于他们提供了一种简单的方法来让对象可以表现的像内置类型一样。那意味着你可以避免丑陋的,违反直觉的,不标准的的操作方法。在一些语言中,有一些操作很常用比如:ifinstance.equals(other_instance):#dosomething在Python中你可以这样。但是这会让人迷惑且产生不必要的冗余。相同的操作因为不同的库会使用不同的名字,这样会产生不必要的工作。然而有了魔术方法的力量,我们可以定义一个方法(本例中为__eq__),就说明了我们的意思:ifinstance==other_instance:#dosomething这只是魔术方法的功能的一小部分。它让你可以定义符号的含义所以我们可以在我们的类中使用。就像内置类型一样。用于比较的魔术方法Python对实现对象的比较,使用魔术方法进行了大的逆转,使他们非常只管而不是笨拙的方法调用。而且还提供了一种方法可以重写Python对对象比较的默认行为(通过引用)。以下是这些方法和他们的作用。__cmp__(self,other)__cmp__是最基本的用于比较的魔术方法。它实际上实现了所有的比较符号(,==,!=,etc.),但是它的表现并不会总是如你所愿(比如,当一个实例与另一个实例相等是通过一个规则来判断,而一个实例大于另外一个实例是通过另外一个规则来判断)。如果selfother的话__cmp__应该返回一个负数,当self==o的时候会返回0,而当selfother的时候会返回正数。通常最好的一种方式是去分别定义每一个比较符号而不是一次性将他们都定义。但是__cmp__方法是你想要实现所有的比较符号而一个保持清楚明白的一个好的方法。__eq__(self,other)定义了等号的行为,==。__ne__(self,other)定义了不等号的行为,!=。__lt__(self,other)定义了小于号的行为,。__gt__(self,other)定义了大于等于号的行为,=。举一个例子,创建一个类来表现一个词语。我们也许会想要比较单词的字典序(通过字母表),通过默认的字符串比较的方法就可以实现,但是我们也想要通过一些其他的标准来实现,比如单词长度或者音节数量。在这个例子中,我们来比较长度实现。以下是实现代码:classWord(str):'''存储单词的类,定义比较单词的几种方法'''def__new__(cls,word):#注意我们必须要用到__new__方法,因为str是不可变类型#所以我们必须在创建的时候将它初始化if''inword:printValuecontainsspaces.Truncatingtofirstspace.word=word[:word.index('')]#单词是第一个空格之前的所有字符returnstr.__new__(cls,word)def__gt__(self,other):returnlen(self)len(other)def__lt__(self,other):returnlen(self)len(other)def__ge__(self,other):returnlen(self)=len(other)def__le__(self,other):returnlen(self)=len(other)现在,我们创建两个Words对象(通过使用Word('foo')和Word('bar')然后通过长度来比较它们。注意,我们没有定义__eq__和__ne__方法。这是因为将会产生一些怪异的结果(比如Word('foo')==Word('bar')将会返回true)。这对于测试基于长度的比较不是很有意义。所以我们退回去,用str内置来进行比较。现在你知道你不必定义每一个比较的魔术方法从而进行丰富的比较。标准库中很友好的在functiontols中提供给我们一个类的装饰器定义了所有的丰富的比较函数。如果你只是定义__eq__和另外一个(e.g.__gt__,__lt__,etc.)这个特性仅仅在Python2.7中存在,但是你如果有机会碰到的话,那么将会节省大量的时间和经理。你可以通过在你定义的类前放置@total_ordering来使用。数值处理的魔术方法如同你在通过比较符来比较类的实例的时候来创建很多方法,你也可以定义一些数值符号的特性。系紧你的安全带,来吧,这里有很多内容。为了组织方便,我将会把数值处理的方法来分成五类:一元操作符,普通算数操作符,反射算数操作符(之后会详细说明),增量赋值,和类型转换。一元操作符和函数仅仅有一个操作位的一元操作符和函数。比如绝对值,负等。__pos__(self)实现正号的特性(比如+some_object)__neg__(self)实现负号的特性(比如-some_object)__abs__(self)实现内置abs()函数的特性。__invert__(self)实现~符号的特性。为了说明这个特性。你可以查看Wikipedia中的这篇文章普通算数操作符现在我们仅仅覆盖了普通的二进制操作符:+,-,*和类似符号。这些符号大部分来说都浅显易懂。__add__(self,other)实现加法。__sub__(self,other)实现减法。__mul__(self,other)实现乘法。__floordiv__(self,other)实现//符号实现的整数除法。__div__(self,other)实现/符号实现的除法。__truediv__(self,other)实现真除法。注意只有只用了from__future__importdivision的时候才会起作用。__mod__(self,other)实现取模算法%__divmod___(self,other)实现内置divmod()算法__pow__实现使用**的指数运算__lshift__(self,other)实现使用的按位左移动__rshift__(self,other)实现使用的按位左移动__and__(self,other)实现使用&的按位与__or__(self,other)实现使用|的按位或__xor__(self,other)实现使用^的按位异或反运算下面我将会讲解一些反运算的知识。有些概念你可能会认为恐慌或者是陌生。但是实际上非常简单。以下是一个例子:some_object+other这是一个普通的加法运算,反运算是相同的,只是把操作数调换了位置:other+some_object所以,除了当与其他对象操作的时候自己会成为第二个操作数之外,所有的这些魔术方法都与普通的操作是相同的。大多数情况下,反运算的结果是与普通运算相同的。所以你可以你可以将__radd__与__add__等价。__radd__(self,other)实现反加__rsub__(self,other)实现反减__rmul__(self,other)实现反乘__rfloordiv__(self,other)实现//符号的反除__rdiv__(self,other)实现/符号的反除__rtruediv__(self,other)实现反真除,只有当from__future__importdivision的时候会起作用__rmod__(self,other)实现%符号的反取模运算__rdivmod__(self,other)当divmod(other,self)被调用时,实现内置divmod()的反运算__rpow__实现**符号的反运算__rlshift__(self,other)实现符号的反左位移__rrshift__(self,other)实现符号的反右位移__rand__(self,other)实现&符号的反与运算__ror__(self,other)实现|符号的反或运算__xor__(self,other)实现^符号的反异或运算增量赋值Python也有大量的魔术方法可以来定制增量赋值语句。你也许对增量赋值已经很熟悉,它将操作符与赋值来结合起来。如果你仍然不清楚我在说什么的话,这里有一个例子:x=5x+=1#inotherwordsx=x+1__iadd__(self,other)实现赋值加法__isub__(self,other)实现赋值减法__im