Python中的魔术方法详解
在Python中,所有以“__”双下划线包起来的方法,都统称为“MagicMethod”,中文称『魔术方法』,例如类的初始化方法__init__,Python中所有的魔术方法均在官方文档中有相应描述,但是对于官方的描述比较混乱而且组织比较松散。很难找到有一个例子。
构造和初始化
每个Pythoner都知道一个最基本的魔术方法,__init__。通过此方法我们可以定义一个对象的初始操作。然而,当调用x=SomeClass()的时候,__init__并不是第一个被调用的方法。实际上,还有一个叫做__new__的方法,两个共同构成了“构造函数”。
__new__是用来创建类并返回这个类的实例,而__init__只是将传入的参数来初始化该实例。
在对象生命周期调用结束时,__del__方法会被调用,可以将__del__理解为“构析函数”。下面通过代码的看一看这三个方法:
fromos.pathimportjoin
classFileObject:
'''给文件对象进行包装从而确认在删除时文件流关闭'''
def__init__(self,filepath='~',filename='sample.txt'):
#读写模式打开一个文件
self.file=open(join(filepath,filename),'r+')
def__del__(self):
self.file.close()
delself.file
控制属性访问
许多从其他语言转到Python的人会抱怨它缺乏类的真正封装。(没有办法定义私有变量,然后定义公共的getter和setter)。Python其实可以通过魔术方法来完成封装。我们来看一下:
1__getattr__(self,name):
定义当用户试图获取一个不存在的属性时的行为。这适用于对普通拼写错误的获取和重定向,对获取一些不建议的属性时候给出警告(如果你愿意你也可以计算并且给出一个值)或者处理一个AttributeError。只有当调用不存在的属性的时候会被返回。
1__setattr__(self,name,value):
与__getattr__(self,name)不同,__setattr__是一个封装的解决方案。无论属性是否存在,它都允许你定义对对属性的赋值行为,以为这你可以对属性的值进行个性定制。实现__setattr__时要避免"无限递归"的错误。
1__delattr__:
与__setattr__相同,但是功能是删除一个属性而不是设置他们。实现时也要防止无限递归现象发生。
1__getattribute__(self,name):
__getattribute__定义了你的属性被访问时的行为,相比较,__getattr__只有该属性不存在时才会起作用。因此,在支持__getattribute__的Python版本,调用__getattr__前必定会调用__getattribute__。__getattribute__同样要避免"无限递归"的错误。需要提醒的是,最好不要尝试去实现__getattribute__,因为很少见到这种做法,而且很容易出bug。
在进行属性访问控制定义的时候很可能会很容易引起“无限递归”。如下面代码:
#错误用法
def__setattr__(self,name,value):
self.name=value
#每当属性被赋值的时候(如self.name=value),__setattr__()会被调用,这样就造成了递归调用。
#这意味这会调用self.__setattr__('name',value),每次方法会调用自己。这样会造成程序崩溃。
#正确用法
def__setattr__(self,name,value):
self.__dict__[name]=value#给类中的属性名分配值
#定制特有属性
Python的魔术方法很强大,但是用时却需要慎之又慎,了解正确的使用方法非常重要。
创建自定义容器
有很多方法可以让你的Python类行为向内置容器类型一样,比如我们常用的list、dict、tuple、string等等。Python的容器类型分为可变类型(如list、dict)和不可变类型(如string、tuple),可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。
在讲创建自定义容器之前,应该先了解下协议。这里的协议跟其他语言中所谓的"接口"概念很像,它给你很多你必须定义的方法。然而在Python中的协议是很不正式的,不需要明确声明实现。事实上,他们更像一种指南。
自定义容器的magicmethod
下面细致了解下定义容器可能用到的魔术方法。首先,实现不可变容器的话,你只能定义__len__和__getitem__(下面会讲更多)。可变容器协议则需要所有不可变容器的所有,另外还需要__setitem__和__delitem__。如果你希望你的对象是可迭代的话,你需要定义__iter__会返回一个迭代器。迭代器必须遵循迭代器协议,需要有__iter__(返回它本身)和next。
1__len__(self):
返回容器的长度。对于可变和不可变容器的协议,这都是其中的一部分。
1__getitem__(self,key):
定义当某一项被访问时,使用self[key]所产生的行为。这也是不可变容器和可变容器协议的一部分。如果键的类型错误将产生TypeError;如果key没有合适的值则产生KeyError。
1__setitem__(self,key,value):
当你执行self[key]=value时,调用的是该方法。
1__delitem__(self,key):
定义当一个项目被删除时的行为(比如delself[key])。这只是可变容器协议中的一部分。当使用一个无效的键时应该抛出适当的异常。
1__iter__(self):
返回一个容器迭代器,很多情况下会返回迭代器,尤其是当内置的iter()方法被调用的时候,以及当使用forxincontainer:方式循环的时候。迭代器是它们本身的对象,它们必须定义返回self的__iter__方法。
1__reversed__(self):
实现当reversed()被调用时的行为。应该返回序列反转后的版本。仅当序列可以是有序的时候实现它,例如对于列表或者元组。
1__contains__(self,item):
定义了调用in和notin来测试成员是否存在的时候所产生的行为。你可能会问为什么这个不是序列协议的一部分?因为当__contains__没有被定义的时候,如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。
1__missing__(self,key):
dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。比如d={'a':1},当你执行d[notexist]时,d.__missing__['notexist']就会被调用。
以上内容为大家介绍了Python中的魔术方法详解,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注IT培训机构:千锋教育。
相关推荐HOT
更多>>python流式读取大文件的两种方法
python流式读取大文件的两种方法1、使用read方法分块读取使用更底层的file.read()方法,与直接循环迭代文件对象不同,每次调用file.read(chunk_...详情>>
2023-11-14 16:48:08pythongreenlet如何交替运行
pythongreenlet如何交替运行1、greenlet说明greenlet是一个基于Greenlet实现的网络库,它使用greenlet来实现协同程序。其基本思想是,当greenle...详情>>
2023-11-14 14:52:57python收集参数的调用顺序
python收集参数的调用顺序本文教程操作环境:windows7系统、Python3.9.1,DELLG3电脑。1、说明收集参数,关键字参数,普通参数可以混合使用使用...详情>>
2023-11-14 13:23:09python实例属性的优先级分析
python实例属性的优先级分析1、说明当在实例上给类属性赋值时,实际上是给这个实例绑定了同名的属性而已,并不会影响类属性和其他实例。使用实...详情>>
2023-11-14 10:17:33