python 魔术方法详解

什么是Python魔术方法

《python 魔术方法详解》

什么是魔术方法呢?当然它跟魔术师没有真正的联系。在面向对象的Python中处处可见。它们是一些可以让你对类添加“魔术”的特殊方法。这些方法在进行特定的操作时会被自动调用,它们是Python面向对象下智慧的结晶。初学者掌握Python的魔法方法也就变得尤为重要。

魔术方法经常由两个下划线包围来命名的。但是现在没有很好的文档来解释它们而且很难找到真正正确的发音,比如__init__,我们叫双下划线init双下划线吗,听起来就好别扭。

那么__init__到底有什么魔术呢?答案是你并不需要直接定义说明。而是在调用类的时候自动调用。比如当我们使用”x = A()”的时候,A类的__new__ 和 __init__方法便会自动调用。

构造和初始化魔术方法

我们都知道最长使用的魔术方法就是__init__,在定义一个类的时候需要声明的初始化方法,但是__init__并不是第一个被使用的魔术方法,在初始化前最先调用的是__new__,这个方法首先创建一个实例,然后再使用__init__初始化实例,在实例的声明周期里面,__del__最后被调用,删除实例。我们这类先来看下这3个魔术方法吧。


__new__(cls, [...)

  __new__是实例化类型中的第一个被调用函数,在调用__new__之后才会传给init,当然__new__使用的比较少,但是具有独特的作用,详情可看python官方文档


__init__(self, [...)

__init__是类实例化的初始化函数,会把构造器里面的初始化内容传入(比如说x = ClassA(10, ‘foo’)),那么在10和’foo’便会作为初始化参数传入类,这是一个几乎所有类都会定义的一个函数


__del__(self)

__new__和__init__都是创建新的类,__del__就是回收器,在一个类被销毁之前调用,它在类被销毁前需要进行其他额外的处理的时候是相当有用的,像socket或者文件。但是需要注意这个函数并不能保证一定能够成功运行,所以在编写程序的时候保持好的编程习惯,关闭文件或者关闭网络连接。

综上,下面是一个使用__init__ 和__del__魔术方法的实例。


from os.path import join
class FileObject:
    '''保证关闭已经打开的文件.'''

    def __init__(self, filepath='./', filename='sample.txt'):
        # 以r+模式打开文件
        self.file = open(join(filepath, filename), 'r+')

    def __del__(self):
        self.file.close()
##关闭文件
        del self.file

自定类使用运算符

魔术方法的一大好处就是可以方便的使得自己定义的类可以像内部定义的类一样,我们可以避免大量的丑陋而不标准的代码。在其他语言我们可能需要


if instance.equals(other_instance):
    # do something

当然在python里面也可以使用,但是python有更优雅的方式__eq__就可以了


def __eq__:
    return instance==other_instance
   

这只是强大的魔术方法一部分,大部分的魔术方法都允许我们定义运算符的含义使得我们自己的函数可以表现得像是内建的函数一样。

比较类魔术方法

python拥有一整套的魔术方法使得我们可以定义大部分的比较方法,我们甚至可以重写内建的函数意义。

< object.__lt__(self, other)
<= object.__le__(self, other)
== object.__eq__(self, other)
!= object.__ne__(self, other)
>= object.__ge__(self, other)
> object.__gt__(self, other)

数字运算符

+ object.__add__(self, other)
object.__sub__(self, other)
* object.__mul__(self, other)
// object.__floordiv__(self, other)
/ object.__truediv__(self, other)
% object.__mod__(self, other)
** object.__pow__(self, other[, modulo])
<< object.__lshift__(self, other)
>> object.__rshift__(self, other)
& object.__and__(self, other)
^ object.__xor__(self, other)
| object.__or__(self, other)

扩展运算符

+= object.__iadd__(self, other)
-= object.__isub__(self, other)
*= object.__imul__(self, other)
/= object.__idiv__(self, other)
//= object.__ifloordiv__(self, other)
%= object.__imod__(self, other)
**= object.__ipow__(self, other[, modulo])
<<= object.__ilshift__(self, other)
>>= object.__irshift__(self, other)
&= object.__iand__(self, other)
^= object.__ixor__(self, other)
|= object.__ior__(self, other)

元运算符

object.__neg__(self)
+ object.__pos__(self)
abs() object.__abs__(self)
~ object.__invert__(self)
complex() object.__complex__(self)
int() object.__int__(self)
long() object.__long__(self)
float() object.__float__(self)
oct() object.__oct__(self)
hex() object.__hex__(self

魔术方法实例

这里我们将定义一个length类,它可以进行长度运算,计算不同的单位


class Length:

    __metric = {"mm" : 0.001, "cm" : 0.01, "m" : 1, "km" : 1000,
                "in" : 0.0254, "ft" : 0.3048, "yd" : 0.9144,
                "mi" : 1609.344 }
    
    def __init__(self, value, unit = "m" ):
        self.value = value
        self.unit = unit
    
    def Converse2Metres(self):
        return self.value * Length.__metric[self.unit]
    
    def __add__(self, other):
        l = self.Converse2Metres() + other.Converse2Metres()
        return Length(l / Length.__metric[self.unit], self.unit )
    
    def __str__(self):
        return str(self.Converse2Metres())
    
    def __repr__(self):
        return "Length(" + str(self.value) + ", '" + self.unit + "')"

if __name__ == "__main__":
    x = Length(4)
    print(x)
    y = eval(repr(x))

    z = Length(4.5, "yd") + Length(1)
    print(repr(z))
    print(z)

输出


4
Length(5.593613298337708, 'yd')
5.1148

__call__ 方法

相信大家写python的时候很多时候都出现“TypeError ‘xxx’ object is not callable”就是因为这个类不能被call,那么定义call方法可以使得自定义的类变成callable的,下面是一个call的实例


class Polynomial:
    
    def __init__(self, *coefficients):
        self.coefficients = coefficients[::-1]
        
    def __call__(self, x):
        res = 0
        for index, coeff in enumerate(self.coefficients):
            res += coeff * x** index
        return res

# a constant function
p1 = Polynomial(42)

# a straight Line
p2 = Polynomial(0.75, 2)

# a third degree Polynomial
p3 = Polynomial(1, -0.5, 0.75, 2)

for i in range(1, 10):
    print(i, p1(i), p2(i), p3(i))

输出


1 42 2.75 3.25
2 42 3.5 9.5
3 42 4.25 26.75
4 42 5.0 61.0
5 42 5.75 118.25
6 42 6.5 204.5
7 42 7.25 325.75
8 42 8.0 488.0
9 42 8.75 697.25

参考:

https://www.python-course.eu/python3_magic_methods.php

https://rszalski.github.io/magicmethods/

点赞