Python神奇方法系统的梳理.doc

上传人:白大夫 文档编号:3271952 上传时间:2019-08-07 格式:DOC 页数:12 大小:45.50KB
返回 下载 相关 举报
Python神奇方法系统的梳理.doc_第1页
第1页 / 共12页
亲,该文档总共12页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《Python神奇方法系统的梳理.doc》由会员分享,可在线阅读,更多相关《Python神奇方法系统的梳理.doc(12页珍藏版)》请在三一文库上搜索。

1、Python神奇方法系统的梳理Python神奇方法是指一些允许在自定义类中增加“神奇”功能的方法。而在Python官方文档中,有关这些方法的介绍或描述不仅内容分散,而且组织结构也相对松散。本文便对Python神奇方法做了系统的梳理。对于初学者亦或Python行家,都或多或少的会有些帮助。话不多说,直接进入主题!Python神奇指南目录简介搭建与初始化在自定义类中运用操作符神奇方法比较神奇方法数字描述自定义类控制属性访问制作自定义序列反射可调用对象上下文管理器构建描述符对象简介何为神奇方法呢?它们是面向Python中的一切,是一些特殊的方法允许在自己的定义类中定义增加“神奇”的功能。它们总是使用

2、双下划线(比如_init_或_lt_),但它们的文档没有很好地把它们表现出来。所有这些神奇方法都出现在Python的官方文档中,但内容相对分散,组织结构也显得松散。还有你会难以发现一个实例(虽然他们被设计很棒,在语言参考中被详细描述,可之后就会伴随着枯燥的语法描述等)。为了弥补Python官方文档的这些缺陷,作者整理了这篇有关magic method的文章,旨在用作教程、复习或参考文档。搭建与初始化相信大家都熟悉这个最基础的神奇方法_init_。它令你能自定义一个对象的初始化行为。而当我调用x=SomeClass()时,_init_并不是最先被调用的。实际上有一个叫做_new_的方法,事实上是

3、它创建了实例,它传递任何参数给初始化程序来达到创建的目的。在对象生命周期结束时,调用_del_。让我们更近地观察下这3个神奇方法吧:_new_(cls,)一个对象的实例化时_new_是第一个被调用的方法。在类中传递其他任何参数到_init_。_new_很少被使用,这样做确实有其目的,特别是当一个子类继承一个不可改变的类型(一个元组或一个字符串)时。_init_(self,)类的初始化。创建对象后,python解释器默认调用_init_()方法。无论主构造函数调用什么,它都会被传递。_init_几乎在Python类定义中普遍使用。_del_(self)如果_new_和_init_构成了对象的构造

4、函数,_ del_就是析构函数。当删除一个对象时,python解释器也会默认调用_del_()方法。在python中,对于开发者来说很少会直接销毁对象(如果需要,应该使用del关键字销毁)。Python的内存管理机制能够很好的胜任这份工作。也就是说,不管是手动调用del还是由python自动回收都会触发_del_方法执行。如下,是是_init_和_del_的例子:fromos.pathimportjoinclassFileObject:对文件对象的包装,确保文件在关闭时得到删除def_init_(self,filepath=,filename=sample.txt):#按filepath,读写

5、模式打开名为filename的文件self.file=open(join(filepath,filename),r+)def_del_(self):self.file.close()delself.file在自定义类中运用操作符神奇方法比较:Python有一大堆magic method,旨在使用运算符实现对象之间的直观比较,而非别扭的方法调用。它们还提供了一种方法来覆盖用于对象比较的默认Python行为。下面是这些方法的列表以及它们的作用:_cmp_(self,other)_cmp_是神奇方法中最基础的一个。实际上它实现所有比较操作符行为( other,则返回正整数。它通常是最好的定义,而不需

6、要你一次就全定义好它们,但当你需要用类似的准则进行所有的比较时,_cmp_会是一个很好的方式,帮你节省重复性和提高明确度。_eq_(self,other)定义了相等操作符,=的行为。_ne_(self,other)定义了不相等操作符,!=的行为。_lt_(self,other)定义了小于操作符,的行为。_le_(self,other)定义了小于等于操作符,=的行为。举一个例子,设想对单词进行类定义。我们可能希望按照内部对字符串的默认比较行为,即字典序(通过字母)来比较单词,也希望能够基于某些其他的准则,像是长度或音节数。在本例中,我们通过单词长度排序,以下给出实现:classWord(str)

7、:单词类,比较定义是基于单词长度的def_new_(cls,word):#注意,我们使用了_new_,这是因为str是一个不可变类型,#所以我们必须更早地初始化它(在创建时)ifinword:print单词内含有空格,截断到第一部分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_le_(self,other):er + som

8、e_object。在大多数情况下,反射算术操作的结果等价于常规算术操作,所以你尽可以在刚重载完_radd_就调用_add_。干脆痛快:_radd_(self,other)实现反射加法_rsub_(self,other)实现反射减法_rmul_(self,other)实现反射乘法_rfloordiv_(self,other)实现反射地板除,用/操作符_rdiv_(self,other)实现传统除法,用/操作符_rturediv_(self,other)实现真实除法,注意,只有当你from _future_ import division时才会有效_rmod_(self,other)实现反射求模,

9、用%操作符_rdivmod_(self,other)实现内置函数divmod()的长除行为,当调用divmod(other,self)时被调用_rpow_(self,other)实现反射乘方,用*操作符_rl列表如下:_rxor_(self,other)实现加法和赋值_isub_(self,other)实现减法和赋值_imul_(self,other)实现乘法和赋值_ifloordiv_(self,other)实现地板除和赋值,用/=操作符_idiv_(self,other)实现传统除法和赋值,用/=操作符_iturediv_(self,other)实现真实除法和赋值,注意,只有当你from

10、_future_ import division时才会有效_imod_(self,other)实现求模和赋值,用%=操作符_ipow_(self,other)实现乘方和赋值,用*=操作符_ilshift_(self,other)实现左按位位移和赋值,使用=操作符_iand_(self,other)实现按位与和赋值,使用_ior_(self,other)实现按位或和赋值,使用|=操作符_ixor_(selfelf)实现到16进制的类型转换_index_(self)实现一个当对象被切片到int的类型转换。如果你自定义了一个数值类型,考虑到它可能被切片,所以你应该重载_index_。_trunc_(

11、self)当math.trunc(self)被调用时调用。_trunc_应当返回一个整型的截断,(通常是long)。_coerce_(self,other)该方法用来实现混合模式的算术。如果类型转换不可能那_coerce_应当返回None。否则,它应当返回一对包含self和other(2元组),且调整到具有相同的类型。描述自定义类用一个字符串来说明一个类这通常是有用的。在Python中提供了一些方法让你可以在你自己的类中自定义内建函数返回你的类行为的描述。_str_(self)当你定义的类中一个实例调用了str(),用于给它定义行为_repr_(self)当你定义的类中一个实例调用了repr(

12、),用于给它定义行为。str()和repr()主要的区别在于它的阅读对象。repr()产生的输出主要为计算机可读(在很多情况下,这甚至可能是一些有效的Python代码),而str()则是为了让人类可读。_unicode_(self)当你定义的类中一个实例调用了unicode(),用于给它定义行为。unicode()像是str(),只不过它返回一个unicode字符串。警惕!如果用户用你的类中的一个实例调用了str(),而你仅定义了_unicode_(),那它是不会工作的。以防万一,你应当总是定义好_str_(),哪怕用户不会使用unicode。_hash_(self)当你定义的类中一个实例调用

13、了hash(),用于给它定义行为。它必须返回一个整型,而且它的结果是用于来在字典中作为快速键比对。_nonzero_(self)当你定义的类中一个实例调用了bool(),用于给它定义行为。返回True或False,取决于你是否考虑一个实例是True或False的。我们已经相当漂亮地干完了神奇方法无聊的部分(无示例),至此我们已经讨论了一些基础的神奇方法,是时候让我们向高级话题移动了。控制属性访问Python通过神奇的方法实现了大量的封装,而不是通过明确的方法或字段修饰符。例如:_getattr_(self,name)你可以为用户在试图访问不存在(不论是存在或尚未建立)的类属性时定义其行为。这对

14、捕捉和重定向常见的拼写错误,给出使用属性警告是有用的(只要你愿意,你仍旧可选计算,返回那个属性)或抛出一个AttributeError异常。这个方法只适用于访问一个不存在的属性,所以,这不算一个真正封装的解决之道。_setattr_(self,name,value)不像_getattr_,_setattr_是一个封装的解决方案。它允许你为一个属性赋值时候的行为,不论这个属性是否存在。这意味着你可以给属性值的任意变化自定义规则。然而,你需要在意的是你要小心使用_setattr_,在稍后的列表中会作为例子给出。_delattr_这等价于_setattr_,但是作为删除类属性而不是set它们。它需要

15、相同的预防措施,就像_setattr_,防止无限递归(当在_delattr_中调用del self.name会引起无限递归)。_getattribute_(self,name)_getattribute_良好地适合它的同伴们_setattr_和_delattr_。可我却不建议你使用它。_getattribute_只能在新式类中使用(在Python的最新版本中,所有的类都是新式类,在稍旧的版本中你可以通过继承object类来创建一个新式类。它允许你定规则,在任何时候不管一个类属性的值那时候是否可访问的。)它会因为他的同伴中的出错连坐受到某些无限递归问题的困扰(这时你可以通过调用基类的_getat

16、tribute_方法来防止发生)。当_getattribute_被实现而又只调用了该方法如果_getattribute_被显式调用或抛出一个AttributeError异常,同时也主要避免了对_getattr_的依赖。这个方法可以使用,不过我不推荐它是因为它有一个小小的用例(虽说比较少见,但我们需要特殊行为以获取一个值而不是赋值)以及它真的很难做到实现0bug。你可以很容易地在你自定义任何类属性访问方法时引发一个问题。参考这个例子:def_setattr_(self,name,value):self.name=value#当每次给一个类属性赋值时,会调用_setattr_(),这就形成了递归#

17、因为它真正的含义是self._setattr_(name,value)#所以这方法不停地调用它自己,变成了一个无法退出的递归最终引发crashdef_setattr_(self,name,value):self._dict_name=value#给字典中的name赋值#在此自定义行为以下是一个关于特殊属性访问方法的实际例子(注意,我们使用super因为并非所有类都有_dict_类属性):classAccessCounter:一个类包含一个值和实现了一个访问计数器。当值每次发生变化时,计数器+1def_init_(self,val):super(AccessCounter,self)._seta

18、ttr_(counter,0)super(AccessCounter,self)._setattr_(value,val)def_setattr_(self,name,value):ifname=value:super(AccessCounter,self)._setattr_(counter,self.counter+1)#Makethisunconditional.#如果你想阻止其他属性被创建,抛出AttributeError(name)异常super(AccessCounter,self)._setattr_(name,value)def_delattr_(self,name)ifnam

19、e=value:super(AccessCounter,self)._setattr_(counter,self.counter+1)super(AccessCounter,self)._delattr_(name)制作自定义序列很有多种方式可以让你的类表现得像内建序列(字典,元组,列表,字符串等)。这些是我迄今为止最喜欢的神奇方法了,因为不合理的控制它们赋予了你一种魔术般地让你的类实例整个全局函数数组漂亮工作的方式。_len_(self)返回容器的长度。部分protocol同时支持可变和不可变容器_getitem_(self,key)定义当某一个item被访问时的行为,使用selfkey表示

20、法。这个同样也是部分可变和不可变容器protocol。这也可抛出适当的异常:TypeError 当key的类型错误,或没有值对应Key时。_setitem_(self,key,value)定义当某一个item被赋值时候的行为,使用selfkey=value表示法。这也是部分可变和不可变容器protocol。再一次重申,你应当在适当之处抛出KeyError和TypeError异常。_delitem_(self,key)定义当某一个item被删除(例如 del selfkey)时的行为。这仅是部分可变容器的protocol。在一个无效key被使用后,你必须抛出一个合适的异常。_iter_(self

21、)应该给容器返回一个迭代器。迭代器会返回若干内容,大多使用内建函数iter()表示。当一个容器使用形如for x in container:的循环。迭代器本身就是其对象,同时也要定义好一个_iter_方法来返回自身。_reversed_(self)当定义调用内建函数reversed()时的行为。应该返回一个反向版本的列表。_contains_(self,item)_contains_为成员关系,用in和not in测试时定义行为。那你会问这个为何不是一个序列的protocol的一部分?这是因为当_contains_未定义,Python就会遍历序列,如果遇到正在寻找的item就会返回True。_

22、concat_(self,other)最后,你可通过_concat_定义你的序列和另外一个序列的连接。应该从self和other返回一个新构建的序列。当调用2个序列时_concat_涉及操作符+在我们的例子中,让我们看一下一个list实现的某些基础功能性的构建。可能会让你想起你使用的其他语言(比如Haskell)。classFunctionalList:类覆盖了一个list的某些额外的功能性魔法,像head,tail,init,last,drop,andtakedef_init_(self,values=None):ifvaluesisNone:self.values=else:self.va

23、lues=valuesdef_len_(self):returnlen(self.values)def_getitem_(self,key):#如果key是非法的类型和值,那么listvaluse会抛出异常returnself.valueskeydef_setitem_(self,key,value):self.valueskey=valuedef_delitem_(self,key):delself.valueskeydef_iter_(self):returniter(self.values)def_reversed_(self):returnreversed(self.values)de

24、fappend(self,value):self.values.append(value)defhead(self):#获得第一个元素returnself.values0deftail(self):#获得在第一个元素后的其他所有元素returnself.values1:definit(self):#获得除最后一个元素的序列returnself.values:-1deflast(last):#获得最后一个元素returnself.values-1defdrop(self,n):#获得除前n个元素的序列returnself.valuesn:deftake(self,n):#获得前n个元素retur

25、nself.values:n反射你也可以通过定义神奇方法来控制如何反射使用内建函数isinstance()和issubclass()的行为。这些神奇方法是:_instancecheck_(self,instance)检查一个实例是否是你定义类中的一个实例(比如,isinstance(instance, class)_subclasscheck_(self,subclass)检查一个类是否是你定义类的子类(比如,issubclass(subclass, class))可调用对象这是Python中一个特别的神奇方法,它允许你的类实例像函数。所以你可以“调用”它们,把他们当做参数传递给函数等等。这是

26、另一个强大又便利的特性让Python的编程变得更可爱了。_call_(self,args)允许类实例像函数一样被调用。本质上,这意味着x()等价于x._call_()。注意,_call_需要的参数数目是可变的,也就是说可以对任何函数按你的喜好定义参数的数目定义_call_。_call_可能对于那些经常改变状态的实例来说是极其有用的。“调用”实例是一种顺应直觉且优雅的方式来改变对象的状态。下面一个例子是一个类表示一个实体在一个平面上的位置:classEntity:描述实体的类,被调用的时候更新实体的位置def_init_(self,size,x,y):self.x,self.y=x,yself.

27、size=sizedef_call_(self,x,y):改变实体的位置self.x,self.y=x,y#省略.上下文管理器上下文管理允许对对象进行设置和清理动作,用with声明进行已经封装的操作。上下文操作的行为取决于2个神奇方法:_enter_(self)定义块用with声明创建出来时上下文管理应该在块开始做什么。_exit_(self,exception_type,exception_value,traceback)定义在块执行(或终止)之后上下文管理应该做什么。你也可以使用这些方法去创建封装其他对象通用的上下文管理。看下面的例子:classCloser:用with声明一个上下文管理用

28、一个close方法自动关闭一个对象def_init_(self,obj):self.obj=objdef_enter_(self):returnself.obj#绑定目标def_exit_(self,exception_type,exception_val,trace):try:self.obj.close()exceptAttributeError:#obj不具备closeprintNotclosable.returnTrue#成功处理异常以下是一个对于Closer实际应用的一个例子,使用一个FTP连接进行的演示(一个可关闭的套接字):frommagicmethodsimportCloser

29、fromftplibimport:;withCloser(FTP(ftp.somsite)asconn:.conn.dir().#省略的输出conn.dir()#一个很长的AttributeError消息,不能关闭使用的一个连接withCloser(int(5)asi:.i+=1.Notcloseable.i6构建描述符对象描述符可以改变其他对象,也可以是访问类中任一的getting,setting,deleting。作为一个描述符,一个类必须至少实现_get_,_set_,和_delete_中的一个。让我们快点看一下这些神奇方法吧:_get_(self,instance,owner)当描述符

30、的值被取回时定义其行为。instance是owner对象的一个实例,owner是所有类。_set_(self,instance,value)当描述符的值被改变时定义其行为。instance是owner对象的一个实例,value是设置的描述符的值_delete_(self,instance)当描述符的值被删除时定义其行为。instance是owner对象的一个实例。现在,有一个有用的描述符应用例子:单位转换策略classMeter(object):米描述符def_init_(self,value=0.0):self.value=float(value)def_get_(self,instance

31、,owner):returnself.valuedef_set_(self,instance,value):self.value=float(value)classFoot(object):英尺描述符def_get_(self,instance,owner):returninstance.meter*3.2808def_set_(self,instance,value):instance.meter=float(value)/3.2808classDistance(object):表示距离的类,控制2个描述符:feet和metersmeter=Meter()foot=Foot()总结这份指南的目标就是让任何人都能读懂它,不管读者们是否具备Python或面向对象的编程经验。如果你正准备学习Python,那你已经获得了编写功能丰富、优雅、易用的类的宝贵知识。如果你是一名中级Python程序员,你有可能已经拾起了一些概念、策略和一些好的方法来减少你编写的代码量。如果你是一名Python专家,你可能已经回顾了某些你可能已经遗忘的知识点,或者你又又有一些新的发现。不管你的经验等级如何,希望你在这次Python神奇方法之旅中有所收获!

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1