在类 C++ 的语言中,类的构造函数和析构函数是非常重要的概念,负责对象的创建和销毁时的相关工作。比如在创建对象时用 new
开辟内存空间,销毁时 delete
释放内存。
而 Python 中似乎没有专门的构造和析构函数,不过也有对应的魔法方法替代构造和析构函数的功能。
Python 学习者最熟悉的魔法方法可能就是 __init__
了,它的作用是初始化对象。
比如说给实例对象的属性进行初始化的工作:
class Foo:
def __init__(self, x):
self.x = x
f = Foo(10)
print(f.x)
# 输出:
# 10
如果父类也实现了 __init__
方法,那么子类中需要显式调用 super()
以确保正确初始化了父类的属性:
class Foo:
def __init__(self, x=10):
self.x = x
class Bar(Foo):
def __init__(self, y):
super().__init__()
self.y = y
f = Bar(20)
print(f.x, f.y)
# 输出:
# 10 20
可能你觉得 __init__
就是构造函数了,但实际上它只负责初始化对象,并不负责创建对象。真正创建对象的是执行得更早的 __new__
方法:
class Foo:
def __new__(cls, *args, **kwargs):
print('new...', args, kwargs)
obj = super().__new__(cls)
print(obj)
return obj
def __init__(self, *args, **kwargs):
print('init...', args, kwargs)
print(self)
Foo('a', x=10)
# 输出:
# new... ('a',) {'x': 10}
# <__main__.Foo object at 0x00000176B9F34AF0>
# init... ('a',) {'x': 10}
# <__main__.Foo object at 0x00000176B9F34AF0>
这里面信息量很大:
__new__
创建了对象,并且执行要早于__init__
。__new__
的第一个参数cls
,就是需要创建实例的类Foo
。__new__
必须返回一个对象,这个对象其实就是创建出来的类实例,也就是self
了。__new__
和__init__
的参数是相同的。
总之,__new__
和 __init__
共同完成了对象的构造工作。
实际上 __new__
方法通常不太会用到。其主要的应用场景归纳如下。
假设现在要定义一个叫“英寸”的类,并且它是 float
的子类,你会怎么做呢?
在 __init__
里实现单位转化是无效的:
class inch(float):
# 错误的方式
def __init__(self, arg):
float.__init__(arg*0.0254)
print(inch(12.0))
# 输出:
# 12.0
因为此时对象已经创建好了,再初始化也没求用。
正确的方式:
class inch(float):
# 正确的方式
def __new__(cls, arg):
return float.__new__(cls, arg*0.0254)
print(inch(12.0))
# 输出:
# 0.3048
其他自定义不可变对象,比如整型、元组等用法也都类似。
单例模式是指全局至多只有一个实例的类。
可以这样实现单例模式:
class Singleton:
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = super().__new__(cls)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
class Bar(Singleton):
pass
first = Bar()
second = Bar()
print(first)
print(second)
# 输出:
# <__main__.Bar object at 0x00000176B9F349A0>
# <__main__.Bar object at 0x00000176B9F349A0>
不过单例模式在 Python 中不那么常见就是了。
用装饰器也可以实现单例模式,在我以前的文章装饰器入门中有探讨。
元编程是指代码动态修改和创建类的编程方式。在元编程的黑魔法里,__new__
方法也占有一席之地。
元编程又是另外一个晦涩的概念了,这里也不展开了,有兴趣的同学看我另一篇文章元类与元编程。
__new__
和 __init__
是对象的构造器,那么 __del__
就是析构器,或者叫销毁器。但需要注意的是,Python 有一套自己的垃圾回收机制,__del__
并不等同于 C 系语言中的析构器,而是更类似于生命周期钩子,它提供了一个对象被销毁时执行自定义逻辑的位置。
比如说你想确保对象被销毁时,文件能够正常关闭:
class FileObject:
# 确保对象被销毁时关闭文件
def __del__(self):
self.file.close()
del self.file
需要小心的是,如果 Python 解释器(或程序)被强行退出时,__del__
并不会被执行。
由于 __del__
可以在执行任意代码时调用,包括从任意线程调用。如果它执行时需要获取锁或者调用任何其他阻塞资源,可能会导致死锁。
所以不要把重要操作完全押宝在上面了,养成定期手动清理资源的好习惯。
本系列文章开源发布于 Github,传送门:Python魔法方法漫游指南
看完文章想吐槽?欢迎留言告诉我!