Python中的魔术方法详解

来源:千锋教育
发布时间:2023-11-07 22:19:01
分享

在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培训机构:千锋教育。

声明:本站部分稿件版权来源于网络,如有侵犯版权,请及时联系我们。

相关推荐

  • 如何使用python中range()函数实现逆序遍历? 一、range函数实现逆序遍历两种实现方式1.先创建一个列表,然后对列表中的元素进行逆序。例如:a=range(4)a=range(4)#[0,1,2,3]new=[]foriinreversed(a
  • python中字典遍历时如何同时获得键和值? 1、item()方法把字典中每对key和value组成一个元组,并把这些元组放在列表中返回。在字典中进行遍历的时候可以采用item()方法将字典中的键值对同时遍历出来。2、语法dict.items()
  • python中如何用字典values() 方法实现字典遍历? 我们经常在使用python中的过程中要对列表进行遍历操作,其实作为python中必不可少的字典也需要遍历。python为字典类型内置了values()方法,以列表形式返回字典中的所有值,该方法会将字典
  • python 的内置字典 python内置了字典:dict(key-value存储方式)的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。>>>d={
  • python 函数的定义 函数数据类型转换>>>int(12.34)12函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:>>>a=abs#变量a指向abs函数>>>a(-1
  • python 可变参数 在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。我们以数学题为例子,给定一组数字a,b,c……,请计算a2+b2+c2+