Python中的魔法方法


一、什么是Python的魔法方法?

魔法方法就是Python的内置方法,不需要主动调用,存在的目的就是给Python的解释器进行调用,几乎每个

魔法方法都有一个对应的内置函数或者运算符,它们经常是使用两个下划线包围来命名的,最为常见的就是

__ init __方法了。总的来说魔法方法就是让我们对类添加魔法的特殊方法(说跟没有说一样)

二、常见的魔法方法

1.构造方法

最为常用的魔法方法就是__ init __方法了,我们可以使用它来指明一个对象初始化的行为。实际上,

当我们再实例化对象时,__ init __方法并不是第一个被调用的方法。事实上应该是new方法,当这个对

象的生命周期结束的时候,__ del __方法会被调用。下面具体阐述

(1)__ new __(cls,[....])

__ new __是对象实例化第一个调用的方法,它只取下cls参数并把其他的参数传递给init方法,它很少使用

(2)__ init __(self,[....])

这是类的初始化方法,它能获取任何传给构造器的参数,这个方法在类的定义中使用到的是最多的

(3)__ del __(self)

new和init都是对象的构造器,__ del __是对象的销毁器,它不是实现了语句del x,而是定义了对象被垃圾

回收时的行为。当对象需要销毁做一些处理的时候这个方法很有用,比如socket对象、文件对象。但是当

Python解释器退出但对象仍然存活的时候,__ del __并不会执行,所以要及时地手工清理对象

下面是一个示例:

from os.path import join

class FileObject:
    #文件对象的装饰类,用来保证文件被删除时能够正确关闭
    def __init__(self,filepath = '~',filename = 'sample.txt'):
        self.file = open(join(filepath,filename),'r+')
        
    def __del__(self):
        self.file.close()
        del self.file

2.运算符

(1)比较运算符

  • __ eq __(self,other) : 定义等于操作符的行为

  • __ ne __(self,other) : 定义不等于操作符的行为

  • __ lt __(self,other) : 定义小于操作符的行为

  • __ gt __(self,other) : 定义大于操作符的行为

  • __ le __(self,other) : 定义小于等于操作符的行为

  • __ ge __(self,other) : 定义大于等于操作符的行为

(2)一元操作符

  • __ pos __(self) : 实现取正的操作

  • __ neg __(self) : 实现取负的操作

  • __ abs __(self) : 实现内建绝对值函数abs()的操作

  • __ invert __(self) : 实现取反的操作

  • __ round __(self) : 实现内建函数round()的操作

  • __ floor __(self) : 实现math.floor()函数的操作

  • __ ceil __(self) : 实现math.ceil()函数的操作

  • __ trunc __(self) : 实现math.trunc()函数的操作,距离零最近的整数

(3)算数操作符

  • __ add __(self,other) : 实现加法的操作

  • __ sub __(self,other) : 实现减法的操作

  • __ mul __(self,other) : 实现乘法的操作

  • __ floordiv __(self,other) : 实现 // 操作符的整数除法

  • __ div __(self,other) : 实现使用 / 操作符的除法

  • __ mod __(self,other) : 实现取余函数的操作

  • __ pow __(self,other) : 实现pow()函数的操作

  • __ lshift __(self,other) : 实现左移位运算符

  • __ rshift __(self,other) : 实现右移运算符

  • __ and __(self,other) : 实现按位与运算符

  • __ or __(self,other) : 实现按位或运算符

  • __ xor __(self,other) : 实现按位异或运算符

(4)类型转换操作符

  • __ int__(self) : 实现到int的类型转换

  • __ float__(self) : 实现到float的类型转换

  • __ complex__(self) : 实现到complex的类型转换

  • __ oct__(self) : 实现到八进制数转换

  • __ hex__(self) : 实现到十六进制数转换

3.类的表示

使用字符串来表示类是一个相当有用的特性,在Python中有一些内建方法可以返回类的表示,也有

一系列魔性方法可以用来自定义在使用这些内建函数时类的行为

  • __ str __(self): 定义对类的实例调用str()的行为

  • __ repr__(self):与str()方法最大的区别就是str()的输出人类可读,repr()机器可读

  • __ unicode __(self):定义对类的实例调用unicode()的行为,返回unicode字符串

  • __ formate __(self): 定义当类的实例用于新式字符串格式化时的行为

  • __ hash __(self): 定义当类的实例调用hash()时的行为,它必须返回一个整数,结果用于字典中键的快速比较

  • __ nonzero __(self): 定义当类的实例调用bool()时的行为

  • __ dir __(self): 定义当类的实例调用dir()时的行为,这个方法会向调用者返回一个属性列表

4.访问控制

Python不是通过显式定义的字段和方法修改器,而是通过魔法方法实现了一系列的封装

  • __ getattr __(self,name):当用户试图访问一个根本不存在的属性时,用户可以通过这个模型方法来定义类

的具体行为

  • __ setattr __(self,name,value): 允许用户自己定义某个属性的赋值行为,不管这个属性是否存在
  • __ delattr __ (self,name) :处理删除属性时的行为,使用时方式产生无限递归

5.自定义序列

有许多方法可以让自定义的Python类表现的像内建序列类型(字典、元组、列表、字符串等);当

想要创建自己的序列类型的时候,需要遵守某些协议。比如:不可变容器必须要定义__ len __方法和

__ getitem __方法;可变容器的协议除了上面提到的两个方法之外,还需要定义 setitem方法和delitem

方法;如果希望元素是可迭代的你需要定义__ iter __,这个方法返回一个迭代器。迭代器必须遵守迭

代器协议,需要定义__ iter __和next方法。容器背后的魔法方法如下:

  • __ len __(self): 返回容器的长度,可变和不可变类型都需要实现
  • __ getitem __(self,key):定义对容器中某一项使用self[key]的方式进行读取操作时的行为,可变和不可变

容器类型都需要实现这个方法。在没有键的类型时产生TypeError异常,没有和键值相匹配时会产生

KeyError异常

  • __ setitem __(self,key):self[key]赋值操作时的行为,可变和不可变容器类型均需要实现,同时在合适的

时候产生KeyError和TypeError异常

  • __ iter __(self,key):返回当前容器的迭代器。迭代器以一连串的内容返回,最常用的就是iter()函数调用,

以及在类似 for i in container:的循环中被调用。迭代器是他们自己的对象,需要定义__ iter __方法

  • __ contains__(self,item): 定义使用in 和 not in 进行成员测试时类的行为

  • __ reversed __(self): 定义了对容器使用 reversed()内建函数的行为,它应该返回一个反转之后的序列

  • __ missing __ (self,key): 定义了当视图访问一个字典中不存在的键时的行为

一个典型的示例:

class FunctionalList:
    #这是一个列表的封装类,实现了一些额外的函数式:head、tail、init、last、drop和take
    
    def __init__(self,values = None):
        if values is None:
            self.values = []
        else:
            self.values = values
            
    def __len__(self):
        return len(self,key)
    def __getitem__(self,key):
        #如果键的类型或值不合法,列表会返回异常
        return self.values[key]
    def __delitem__(self,key):
        del self.values[key]
        
    def __iter__(self):
        return iter(self.values)
    def __reversed__(self):
        return reversed(self.values)
    def append(self,value):
        self.values.append(value)
    def head(self):
        return self.values[0]
    def tail(self):
        return self.values[1:]  #取得除第一个元素外的所有元素
    def init(self):
        return self.values[:-1]  #取得除最后一个元素外的所有元素
    def last(self):
        #取得最后一个元素
        return self.values[-1]
    def drop(self,n):
        #取得除前n个元素外的所有元素
        return self.values[n:]
    def take(self,n):
        return self.values[:n]

6.反射

  • __ instancecheck __ (self,check): 检查一个实例是否是你定义的类的一个实例

  • __ subclasscheck__(self,subclass):检查一个类是否是你定义类的子类

7.可调用对象

在Python中,函数也是一种对象,也就意味着它们可以像其他任何对象一样被传递到函数或者方法中,

  • __ call __(self,[args....]): 允许一个类的实例像函数那样被调用,一般用于需要经常改变状态的类的实例

8.拷贝

当你想要改变一个对象而且不影响原有的对象时,可以使用Python中的copy模块

  • __ copy __(self):定义对类的实例使用copy.copy()时的行为;copy.copy()返回一个对象的浅拷贝,即拷贝

出的实例是全新的,然而里面的数据全是引用的,浅拷贝中的数据更改会影响原对象

  • __ deepcopy __(self,memodict=): 定义对类的实例使用deepcopy()时的行为。deepcopy()会返回一个

对象的深拷贝,这个对象和它的数据全都被拷贝了一遍。memodict是一个先前拷贝对象的缓存,它优化

了拷贝的过程,可以防止拷贝递归数据结构时产生无限递归;如果你想要拷贝一个单独的属性时,在那个

属性上调用copy.deepcopy()函数,使用memodict作为第一个参数

10.Pickling

Pickling是Python数据结构的序列化过程,当你想要存储一个对象再取出读取时,Pickling会显得十分有用

Pickle模块=不仅仅可以用于内建类型,任何遵守pickle协议的类都可以被pickle。Pickle协议有四个可选方法

  • __ getnewargs__(self):通过这个方法改变类在反pickle时传递new函数的参数,返回一个参数元组
  • __ getstate __ (self): 可以自定义对象被pickle的状态,而不是使用对象的dict属性;这个状态在反pickle时

会被__ setstate__使用

  • __ setstate__ :和getstate方法相互依存;当这两个对象均被定义时,可以在pickle时使用任何方法保存对象

的任何状态

方法使用示例:

class Slate:
    #存储一个字符串和一个变更日志的类 每次被pickle都会忘记当前值
    def __init__(self,value):
        self.value = value
        self.last_change = time.asctime()
        self.history = {}
    def change(self,new_value):
        #改变当前值。将上一个值记录到历史
        self.history[self.last_change] = self.value
        self.value = new_value
        self.last_change = time.asctime()
    def __getstate__(self):
        #不返回self.value或者self.last_change
        #反pickle时得到一个空白的slate
        return self.history
    def __setstate__(self):
        self.history = state
        self.value,self.last_change = None,None