Python进阶-魔法方法实例分析

1.简介

在上一篇文章中(http://kuaibao.qq.com/s/20190216A0N9OW00)我们已经讲解了Python魔法方法的定义形式和使用魔法方法带来的好处,由此可知深入理解魔法方法对于进阶学者的重要性。从上文的思维导图中可知,Python魔法方法非常多,但只有部分魔法方法是非常常用的,作为一个Python高级开发人员是需要准确的理解并经常使用的。

2.魔法方法深入分析和实践

2.1 构造函数和构造函数

每一个Python开发者都应该知道一个最基本的魔术方法, __init__(self,..) ,此方法的用处是定义一个对象的初始操作。然而,当调用 x = SomeClass() 的时候, __init__(self) 并不是第一个被调用的方法。实际上,还有一个叫做__new__ 的方法,两个共同构成了“构造函数”。__new__(cls,)是用来创建类并返回这个类的实例, 而__init__只是将传入的参数用来初始化该实例。在对象生命周期调用结束时,__del__ (self)方法会被调用,可以将__del__理解为“构析函数”。

def __new__(cls, *args, **kwargs)

__new__() 方法是在类准备将自身实例化时调用;

__new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。

图1:__init__和__new__执行顺序比较

第一个参数cls是当前正在实例化的类。如果要得到当前类的实例,应当在当前类中的 __new__() 方法语句中调用当前类的父类的 __new__() 方法。通常来说,新式类开始实例化时,__new__()方法会返回cls(cls指代当前类)的实例,然后该类的__init__()方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入__new__()方法中接收的位置参数和命名参数。

上面讲解创建一个对象时,构造函数执行过程,那么当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()魔法方法。在python中对于开发者来说很少会直接销毁对象(如果需要,应该使用del关键字销毁)。Python的内存管理机制能够很好的胜任这份工作。也就是说,不管是手动调用del还是由python自动回收都会触发__del__方法执行。

2.2 字符串魔法函数__str__

__str__:在将对象转换成字符串str(对象)测试的时候,打印对象的信息,__str__方法必须要return一个字符串类型的返回值,作为对实例对象的字符串描述,__str__实际上是被print函数默认调用的,当要print(实例对象)时,默认调用__str__方法,将其字符串描述返回。如果不是要用str()函数转换。当你打印一个类的时候,那么print首先调用的就是类里面的定义的__str__。

图2:__str__魔法方法

2.3 __repr__

__repr__:如果说__str__体现的是一种可读性,是给用户看的,那么__repr__方法体现的则是一种准确性,是给开发人员看的,它对应的是repr()函数,重构__repr__方法后,在控制台直接敲出实例对象的名称,就可以按照__repr__中return的值显示了。

打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),它通常应该返回一个友好的显示。__repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。当我们想所有环境下都统一显示的话,可以重构__repr__方法;当我们想在不同环境下支持不同的显示,例如终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示,实际上__str__只是覆盖了__repr__以得到更友好的用户显示。

图3:__repr__魔法方法

2.4 __getattr__

__getattr__定义当用户试图获取一个不存在的属性时的行为。可以很方便地动态返回一个属性;当调用不存在的属性时,Python会试图调用__getattr__(self,'key')来获取属性,并且返回key。

图4:__getattr__魔法方法

2.5 __setattr__

与__getattr__(self, name)不同,__setattr__ 是一个封装的解决方案。无论属性是否存在,它都允许你定义对对属性的赋值行为,因为这你可以对属性的值进行个性定制。

图5:__setattr__魔法方法

2.6 __getattr__

__getattribute__:属性访问拦截器,在访问实例属性时自动调用。在python中,类的属性和方法都理解为属性,且均可以通过__getattribute__获取。当获取属性时,相当于对属性进行重写,直接return object.__getattribute__(self, *args, **kwargs)或者根据判断return所需要的重写值,如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可。如果不加的话,返回的是函数引用地址。Python中只要定义了继承object的类,就默认存在属性拦截器,只不过是拦截后没有进行任何操作,而是直接返回。所以我们可以自己改写__getattribute__方法来实现相关功能,比如查看权限、打印log日志等。

2.7 上下文管理魔法方法

with open('/home/user/testfile.txt') as testfile:

#do somethig with testfile

使用with来管理上下文,我们可以做一些对象的开始操作和退出操作,还能对异常进行处理。这需要实现两个魔术方法: __enter__ 和 __exit__。

__enter__(self):定义了当使用with语句的时候,会话管理器在块被初始创建时要产生的行为。请注意,__enter__的返回值与with语句的目标或者as后的名字绑定。

__exit__(self, exception_type, exception_value, traceback):定义了当一个代码块被执行或者终止后,会话管理器应该做什么。它可以被用来处理异常、执行清理工作或做一些代码块执行完毕之后的日常工作。如果代码块执行成功,exception_type,exception_value,和traceback将会为None。否则,你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,请确保__exit__在所有语句结束之后返回True。如果你想让异常被会话管理器处理的话,那么就让其产生该异常。

2.8 自定义容器魔法方法

Python定义容器可能用到的魔术方法。首先,实现不可变容器的话,你只能定义 __len__ 和 __getitem__ 。可变容器协议则需要所有不可变容器的所有,另外还需要 __setitem__ 和 __delitem__ 。如果你希望你的对象是可迭代的话,你需要定义 __iter__ 会返回一个迭代器。迭代器必须遵循迭代器协议,需要有 __iter__(返回它本身) 和 next。

__len__(self):返回容器的长度。对于可变和不可变容器的协议,这都是其中的一部分。

__getitem__(self,key):定义当某一项被访问时,使用self[key]所产生的行为。这也是不可变容器和可变容器协议的一部分。如果键的类型错误将产生TypeError;如果key没有合适的值则产生KeyError。

__setitem__(self, key, value):当你执行self[key] = value时,调用的是该方法。

__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或者False。

__missing__(self, key):dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。比如d = {'a': 1}, 当你执行d[notexist]时,d.__missing__['notexist']就会被调用。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190218A0A1MG00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券