前面的话:在Pyhton中,我们经常可以看到以双下滑线__包裹起来的方法,最常见的就是__init__
,这些方法被称为魔法方法或者特殊方法。简单的说,这些方法可以给Python的类提供特殊功能。方便我们定制一个类。定义一个类时,我们用的最多就是__init__
方法,而__new__
方法和__call__
方法使用的比较少。下面我们介绍最常用的几个魔法方法。
一、 __init__
方法
__init__
方法用于控制实例对象的初始化工作,也就是给实例对象绑定属性,以初始化对象的特征。要不生成的对象在人类的世界是一样的,因为并没有特殊的标识,但每个对象在内存中的地址是相互独立的,也就是说并不是同一个对象。
另外要特别注意点是:这个方法并不是类实例化过程中调用的第一个方法。因为系统执行该方法前,其实该对象已经存在了,要不初始化什么东西呢?
class Magic(object):
def __init__(self):
print("__init__ is called")
super(Magic, self).__init__()
def __new__(cls):
print("__new__ is called")
return super(Magic, cls).__new__(cls)
def __call__(self):
print("__call__ is called")
instance = Magic()
输出:
从输出结果来看,__new__
方法最先被调用,返回一个实例对象,接着__init__
方法被调用。
稍微修改下代码:
class Magic(object):
def __init__(self):
print("__init__ is called")
print(self)
super(Magic, self).__init__()
def __new__(cls):
print("__new__ is called")
self = super(Magic, cls).__new__(cls)
print(self)
return self
def __call__(self):
print("__call__ is called")
instance = Magic()
输出:
从输出来看,__new__
方法的返回值就是类的实例对象,这个实例对象会传递给__init__
方法中定义的self参数,以便实例对象可以被正确的初始化。
如果__new__
方法不返回值(或者说返回None),那么__init__
方法将不会得到调用。因为实例对象还没有创建出来,调用__init__
是没有任何意义的。
另外,__init__
方法中除了self之外定义的参数,都将与__new__
方法中除cls参数以外的所有参数都必须保持一致或者等效。
二、__new__
方法
__new__
方法用于控制类实例化过程中对象的生成工作,它是一个类级别的方法。
最基本的作用:就是当你继承一些不可变class的时候(比如int、tuple),提供给你一个自定义这些类的实例化过程的途径,还有就是实现自定义的metaclass。
首先我们来看第一个功能,假设我们需要一个永远是正数的整数类型。我们可能会写出这样的代码。
class PositiveInteger(int):
def __init__(self, value):
super(PositiveInteger, self).__init__(self, abs(value))
i = PositiveInteger(-3)
print(i)
但运行后发现,对于int这种不可变对象,我们只有重载它的__new__
方法才能起到自定义的作用。
class PositiveInteger(int):
def __new__(cls, value):
return super(PositiveInteger, cls).__new__(cls, abs(value))
i = PositiveInteger(-2)
print(i)
通过重载__new__
方法,我们实现了所要的功能。
另一个作用,就是自定义metaclass。
通过__new__
实现设计模式中的单例模式。因为类每一次实例化后产生的过程都是通过__new__
来控制的,所以我们可以通过重载__new__
方法。就可以很简单的实现单列模式。
class SingLeton(object):
def __new__(cls):
if not hasattr(cls, "instance"):
cls.instance = super(SingLeton, cls).__new__(cls)
return cls.instance
obj1 = SingLeton()
obj2 = SingLeton()
obj1.attr1 = "value1"
print(id(obj1))
print(id(obj2))
print(obj1.attr1, obj2.attr1)
print(obj1 is obj2)
可以看到obj1和obj2是同一个实例。
这就是通过__new__
方法实现单例模式的一种方式,如果实例对象存在,则直接返回该实例对象,如果不存在,那么就先创建一个实例对象,再返回。
三、__call__
方法
用于使一个实例对象成为一个可调用对象,就像函数那样可以调用(函数名())。
下面先来解释什么是可调用对象:可调用对象(callable),我们平时自定的函数,内置函数和类都是可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象。判断一个对象是不是可调用对象可以用函数callable
所以说,如果一个类中,实现了__call__
方法,那么由这个类生成的实例对象就是一个可掉用对象。
所以此时,这个instance对象既是一个实例对象,又是一个可调用对象,我们可以像函数一样调用它。
那么这个特点有哪些应用场合了:实例对象可以像函数一样作为可调用对象来用,基于这个特点,我们可以实现基于类的装饰器,在类里面记录状态,比如,我们可以记录函数被调用的次数。
class Counter(object):
def __init__(self, fun):
self.fun = fun
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
return self.fun(*args, **kwargs)
@Counter
def foo():
pass
for i in range(10):
foo()
print(foo.count)
上面这么解释可能不是很好理解,这是因为我们定义了一个无参的类装饰器,所以我们调用函数时,会把foo函数作为参数传给Counter这个类,然后实例化了一个实例对象(foo),因此,我们现在调用foo时,实际上是调用的这个实例对象,进而去调用这个类中的__call__
方法。达到计数的功能。
四、__del__
方法
__del__
方法:用于删除一个对象,这个方法有一个专业术语——析构函数。Python类似Java语言是有垃圾回收机制的,所以不需要像C++那样通过析构函数手动释放内存。但python同样提供了`__del__
释放方法。当一个对象的引用计数为0时,会被自动调用,以清除这个对象。
都知道python是一门脚本语言,解释型语言,但Python其实是有编译过程的。所以说的细致一点python是一门先编译后解释的语言,编译指的是编译成字节码,然后逐行解释这些字节码。
所以整个过程是这样的:过程.py->编译->PyCodeObject->解释(虚拟机执行)