一个简单的单例模式:
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self, name, *args, **kwargs):
self.name = name
...
args = []
kwargs = {}
x = Singleton('xx', *args, **kwargs)
y = Singletom('yy')
id(x) == id(y) #=> True
Python 中可以通过 __new__
方法实现单例模式。
在上面的例子中,调用 Singleton('xx')
创建单例对象的具体步骤是:
- 调用
Singleton
类的静态__new__
方法创建一个Singleton
实例- 将参数列表
('xx', *args, **kwargs)
传递给__new__
方法,所以最好给__new__
方法加上(cls, *args, **kwargs)
参数以保证其能接收所有传入的实参,cls
表示这是一个类的静态方法,方法内的所有操作都不能基于实例,因为实例还未创建。这里cls
指的就是类Singleton
本身 hasattr
函数用来判断类或者实例上是否定义了某个属性(类属性或者实例属性),这里通过判断类属性instance
是否存在,来确定该类是否存在实例。类属性instance
不存在意味着该类尚未实例化出任何一个对象,此时调用父类的__new__
方法构造一个实例并将其保存在类属性instance
中,当下次尝试实例化该类时,发现类属性instance
已经存在,此时就会直接返回第一次构造的实例,即cls.instance
。__new__
方法构造了一个指定类的实例,并且需要返回这个实例。需要注意的是,只有__new__
方法返回当前类的实例时才能正确调用接下来的__init__
方法,不返回或者返回其它类的实例都不会调用自定义的__init__
方法。- 因为当前类继承了父类(直到继承
object
类),所以当前类的__new__
方法构造的(未初始化的)实例实际上与父类的(初始化后的)对象拥有相同的属性和方法,因此__new__
方法返回的实例是通过父类的__new__
方法构造出的实例,__new__
方法的第一个参数指定了返回的实例的类型,通常情况下该参数应该是cls
,以确保__new__
方法返回的是当前类的一个实例,这样才能成功执行自定义的__init__
方法。
- 将参数列表
- 调用
Singleton
实例的__init__
方法完成初始化__init__
方法在__new__
方法返回该类的实例后自动执行(__new__
方法不返回或者返回其它类的实例时则不会执行)__init__
方法的第一个参数是self
,表示它是一个实例的方法,这个实例就是__new__
方法返回的实例;self
代表了 实例 或者叫 对象。- 参数列表
('xx', *args, **kwargs)
会再次传递给__init__
方法,可以设计自己设计__init__
方法的形参,只要保证所有的实参能被正确接收即可。 - 对当前的实例进行初始化,添加额外的属性等。正是
__init__
方法给实例添加了额外的属性,使得此实例比父类的对象内容更多,结构更复杂,达到抽取公共属性和方法的目的。
__new__
方法用于 构造实例,它是类的静态方法,可以使用类的静态方法和属性,也可以定义类的属性。通常 __new__
方法会调用父类的 __new__
方法并指定 cls
来构造一个本类的实例。__new__
方法最后还需要返回本类的实例,才能使 __init__
方法自动调用。
如果 __new__
方法返回的是其它类的实例,__init__
方法不会被调用,最终返回给用户的对象也是其他类的实例,并且这个实例是不完整的,因为它的 __init__
方法没有被正确调用,看这个例子(https://zhuanlan.zhihu.com/p/279393180):
class B(object):
def __new__(cls):
int("B __new__ called")
return super().__new__(cls)
def __init__(self):
print("B __init__ called")
self.attr='B'
def test(self):
print("B test")
class C(object):
def __new__(cls):
print("C __new__ called")
return super().__new__(B)
def __int__(self):
print("C __init__ called")
self.attr="C"
def test(self):
print("C test")
c=C()
c.test()
print(c.attr)
打印结果为:
C __new__ called
B test
Traceback (most recent call last):
File ...
print(c.attr)
AttributeError: 'B' object has no attribute 'attr'
C.__new__
返回了 super().__new__(B)
,表示使用 C
类的父类(这里是 object
)构造了一个实例,并将其转换为为 B
类型后返回。最终用户取得的 c
实际上是 B
类的一个实例,并且构造这个实例时没有调用其 __init__
方法。调用 c.test()
实际上调用的是 B
类实例的 .test()
方法。
因为 C.__new__
没有正确返回 C
类的实例,导致该实例不会自动执行 C
类中为实例初始化准备的 __init__
方法,同时该实例也不会自动执行 B
类的 __init__
方法,导致该实例实际上没有定义 attr
属性,因此访问 c.attr
报错。
评论区