常用的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类的魔术方法"