每个人都知道一个最基本的魔术方法, __init__
。通过此方法我们可以定义一个对象的初始操作。然而,当我调用 x =SomeClass()
的时候, __init__
并不是第一个被调用的方法。实际上,还有一个叫做 __new__
的方法,来构造这个实例。然后给在开始创建时候的初始化函数来传递参数。在对象生命周期的另一端,也有一个 __del__
方法。我们现在来近距离的看一看这三个方法:
__new__(cls, [...)
__new__
是在一个对象实例化的时候所调用的第一个方法。它的第一个参数是这个类,其他的参数是用来直接传递给 __init__
方法。 __new__
方法相当不常用,但是它有自己的特性,特别是当继承一个不可变的类型比如一个tuple或者string。我不希望在 __new__
上有太多细节,因为并不是很有用处,但是在 Python文档 中有详细的阐述。
__init__(self, […)
此方法为类的初始化方法。当构造函数被调用的时候的任何参数都将会传给它。(比如如果我们调用x = SomeClass(10, 'foo')
),那么 __init__
将会得到两个参数10和foo。 __init__
在Python的类定义中被广泛用到。
__del__(self)
如果 __new__
和 __init__
是对象的构造器的话,那么 __del__
就是析构器。它不实现语句 del x
(以上代码将不会翻译为 x.__del__()
)。它定义的是当一个对象进行垃圾回收时候的行为。当一个对象在删除的时需要更多的清洁工作的时候此方法会很有用,比如套接字对象或者是文件对象。注意,如果解释器退出的时候对象还存存在,就不能保证 __del__
能够被执行,所以 __del__
can’t serve as a replacement for good coding practices ()~~~~~~~
放在一起的话,这里是一个 __init__
和 __del__
实际使用的例子。
from os.path import join
class FileObject:
'''给文件对象进行包装从而确认在删除时文件流关闭'''
def __init__(self, filepath='~', filename='sample.txt'):
#读写模式打开一个文件
self.file = open(join(filepath, filename), 'r+')
def __del__(self):
self.file.close()
del self.file
继承自object的新式类才有__new__
__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
例:
>>> class E(object):
def __init__(self):
print "init"
def __new__(cls,*args,**kwargs):
print "new %s"%cls
return super(E, cls).__new__(cls, *args,**kwargs)
>>> te = E()
new <class '__main__.E'>
init
>>>
>>> print type(te)
<class '__main__.E'>
或者
>>> class D(object):
def __init__(self):
print "init"
def __new__(cls,*args,**kwargs):
print "new %s"%cls
return object.__new__(D,*args,**kwargs)
>>> td = D()
new <class '__main__.D'>
init
>>> print type(td)
<class '__main__.D'>
若__new__没有正确返回
当前类cls
的实例,那__init__是不会被调用的,即使是父类的实例也不行
>>> class B(object):
def __init__(self):
print "init"
>>> class C(object):
def __init__(self):
print "init"
def __new__(cls,*args,**kwargs):
print "new %s"%cls
return object.__new__(B,*args,**kwargs)
>>> tc = C()
new <class '__main__.C'>
>>> print type(tc)
<class '__main__.B'>
若没有继承object,也会自动创建对象,但不会执行本身的__new__方法,至于是否调用了object的__new__方法有待探究
>>> class G():
def __init__(self):
print "init"
def __new__(cls,*args,**kwargs):
print "new %s"%cls
return object.__new__(G,*args,**kwargs)
>>> tg = G()
init
>>> print type(tg)
<type 'instance'>
>>> isinstance(tg,G)
True
__new__ 的作用
依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。
首先我们来看一下第一个功能,具体我们可以用int来作为一个例子:
假如我们需要一个永远都是正数的整数类型,通过集成int,我们可能会写出这样的代码。
class PositiveInteger(int):
def __init__(self, value):
super(PositiveInteger, self).__init__(self, abs(value))
i = PositiveInteger(-3)
print i
但运行后会发现,结果根本不是我们想的那样,我们任然得到了-3。这是因为对于int这种 不可变的对象,我们只有重载它的__new__方法才能起到自定义的作用。
这是修改后的代码:
class PositiveInteger(int):
def __new__(cls, value):
return super(PositiveInteger, cls).__new__(cls, abs(value))
i = PositiveInteger(-3)
print i
通过重载__new__方法,我们实现了需要的功能。
另外一个作用,关于自定义metaclass。其实我最早接触__new__的时候,就是因为需要自定义 metaclass,但鉴于篇幅原因,我们下次再来讲python中的metaclass和__new__的关系。
用__new__来实现单例
事实上,当我们理解了__new__方法后,我们还可以利用它来做一些其他有趣的事情,比如实现 设计模式中的 单例模式(singleton) 。
因为类每一次实例化后产生的过程都是通过__new__来控制的,所以通过重载__new__方法,我们 可以很简单的实现单例模式。
class Singleton(object):
def __new__(cls):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
obj1.attr1 = 'value1'print obj1.attr1, obj2.attr1
print obj1 is obj2
输出结果:
value1 value1
True