常用的python类的魔术方法

BG16

Posted by Blue Geek on September 30, 2019

常用的python类的魔术方法

By 青衣极客 Blue Geek In 2019-09-30

对于很少使用python编写大型代码的朋友可能会忘记python还是一种面向对象的语言。在其他面向对象的语言中有构造函数、析构函数等等在生命周期不同时机自动调用的函数,python当然也是有的。除此之外,python类还有很多神奇的编写方式让对象的表现更加丰富,这些方法也被称为魔术方法。在 python中魔术方法还是比较多的,这里只挑最常用的几个来讨论一下。

1. 与生命周期有关的魔术方法

所谓生命周期是指事物产生、运行和消亡等存在过程中的若干特殊的时机。对于面向对象而言,常常被谈到的就是构造函数和析构函数。python中提供了__new__函数作为构造函数,即在对象创建时调用的函数;__del__函数作为析构函数,即在对象被销毁时调用的函数;python还提供了__init__函数在初始化的时候被调用。

class A:
    # 初始化函数
    def __init__(self, arg=1):
        print('initialize')
    
    # 构造函数
    def __new__(cls, *args, **kwargs):
        print('construct')
        return object.__new__(cls, *args, **kwargs)
    
    # 析构函数
    def __del__(self):
        print('delete')
a = A()
a = 1
construct
initialize
delete

从以上的演示中可以看出,构造函数__new__最先被调用,其次是__init__初始化函数,最后在对象被销毁时调用了__del__函数。知道了这些函数运行的时机就可以利用它们做一些有意义的事情。最常用的是__init__函数,一般在这个函数中进行对象成员的初始化设置;其次是__del__析构函数,一般用于关闭一些占用的资源,避免资源泄漏;最后__new__函数非常不常用,一般而言可以不用了解。

2. 与比较相关的魔术方法

在其他面向对象的语言中有操作符重载来实现对象之间的比较功能,python没有直接使用操作符重载,而是使用几个魔术函数来实现的。__gt__函数用于实现“大于号”比较,__lt__用于实现“小于号”比较,__ge__用于实现“大于等于”的比较,__le__用于实现“小于等于”的比较。

class B:
    
    def __init__(self, real, img):
        self.real = real
        self.img = img
    
    def mod(self):
        import math
        return math.sqrt(self.real**2+self.img**2)
    
    def __gt__(self, b):  # 大于
        return self.mod() > b.mod()
    def __lt__(self, b):  # 小于
        return self.mod() < b.mod()
    def __ge__(self, b):  # 大于等于
        return self.mod() >= b.mod()
    def __le__(self, b):  # 小于等于
        return self.mod() <= b.mod()
    
print(B(1,1)>B(1,1))
print(B(1,1)<B(1,1))
print(B(1,1)>=B(1,1))
print(B(1,1)<=B(1,1))
False
False
True
True

3. 与数值计算相关魔术方法

有时也有一些数值计算方面的处理需要进行简化。python提供了__abs__函数来实现取绝对值操作abs(),__pos__函数实现取“正”操作,__neg__函数实现取“负“操作。

class C:
    def __init__(self, real, img):
        self.real = real
        self.img = img
        
    def __abs__(self):  # abs函数调用
        import math
        return math.sqrt(self.real**2+self.img**2)
    def __pos__(self):  # +obg调用
        return self
    def __neg__(self):  # -obj调用
        return C(-self.real, -self.img)
    
    def p(self):
        print('real={}, img={}'.format(self.real, self.img))
    
print(abs(C(1,2)))
(+C(1,2)).p()
(-C(1,2)).p()
2.23606797749979
real=1, img=2
real=-1, img=-2

4. 与调用相关的魔术方法

python中有一些很常用的函数,比如len,str等等,我们常常需要对一些对象使用这种操作。如果是自定义的类型,就需要实现一些魔术方法达到目的。__str__函数用于str()的调用,__hash__函数用于hash()的调用,__len__函数用于len()的调用,__call__函数用于实现以对象自己作为函数名来进行调用。熟悉了这些魔术方法之后就可以自定义更加通用的类型。

class D:
    def __init__(self, name='hello', year=10):
        self.name = name
        self.year = year
    
    def __str__(self):  # str函数调用
        return '__str__<{}:{}>'.format(self.name, self.year)
    # 已经废弃
    def __unicode__(self):
        return u'__unicode__[{}:{}]'.format(self.name, self.year)
    def __hash__(self): # hash函数调用
        return len(self.name)+self.year
    def __len__(self):  # len函数调用
        return len(self.name)
    def __call__(self): # 对象直接作为函数调用
        return '__call__*{}:{}*'.format(self.name, self.year)

d = D()
print(str(d))
print(hash(d))
print(len(d))
print(d())
__str__<hello:10>
15
5
__call__*hello:10*

5. 与语法相关的魔术方法

循环是各种编程语言都比较常用的语法,而python中的循环就更加简便。python中的序列结构都可以直接放在for循环中进行遍历,那么自定义的类型可不可以也做成这样的呢?当然可以,就是用过实现__iter__ 和 __next__两个函数来达到目的的。

python中还有一种很常用的语法叫“with子句”,这种语法的方便之处就是使用完资源的句柄之后会自动释放,不需要显式手动close。要让自己定义的数据类型能够在这样的语法场景下使用就需要实现__enter__和__exit__两个函数。其中__enter__函数是在with子句创建的时候调用的,而__exit__函数就是在with子句结束时自动调用来释放占用的资源。

class E:
    def __init__(self, name='hello'):
        self.name = name
        self.count = 0
    # 循环迭代器需要实现的两个函数
    def __iter__(self): # 返回迭代器
        self.count = 0
        return self
    def __next__(self): # 循环调用该方法
        self.count += 1
        if self.count > len(self.name):
            raise StopIteration
        else:
            return self.name[self.count-1]
    # with子句需要实现的两个函数
    def __enter__(self):
        print('with子句')
        return self.name
    def __exit__(self, e_type, e_v, t):
        print('关闭资源')
        return True

for i in E():
    print(i)

with E() as e:
    print(e)
h
e
l
l
o
with子句
hello
关闭资源

到此,一些常用的魔术函数就介绍完毕。对于python的初学者而言,应该是会有一些启发作用。但是python中的魔术方法远不止于此,对于想要了解更多魔术方法的朋友,本文也可以起一个抛砖引玉的作用。

【青衣极客】公众号



COMMENT

博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题

"BG16-常用的python类的魔术方法"