Python单例模式已经实现上的一些坑(语法层面)
原理及代码实现
实现环境:Python3.7
,也就是说定义类的时候会默认继承object
类的
原理
Python在实例化对象时会先调用__new__
方法,所以在那里拦截住就行。
实现
经过一番百度,找到实现:
class Singleton1:
def __init__(self, a, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton1, cls).__new__(cls)
return cls._instance
挖坑与跳坑
看到上述代码,手有点痒,所以就改了一下,那个__new__
里面能不能传递*args
,**kwargs
呢,说做就做,
class Singleton1:
def __init__(self, a, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton1, cls).__new__(cls, *args, **kwargs)
return cls._instance
然而现实是:TypeError: object.__new__() takes exactly one argument (the type to instantiate)
错误的原因是其父类object
的__new__
方法只有一个参数。于是我特地去看了下,是这么写的:
@staticmethod # known case of __new__
def __new__(cls, *more): # known special case of object.__new__
""" Create and return a new object. See help(type) for accurate signature. """
pass
我感到很疑惑,因为学习单例模式时我是用pycharm
写的代码,我敲__new__
后会提示有def __new__(cls, *args, **kwargs)
,这就真奇怪了,难道pycharm
提示错了?感觉可能性微乎其微。又经过一番百度,可以这样写
class Singleton1:
def __init__(self, a, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
def __new__(cls, a, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton1, cls).__new__(cls, *args, **kwargs)
return cls._instance
如果debug一下可以发现,你在实例化的时候,比如s = Singleton1(1, 2)
会将参数全部带到__new__
中去,从而导致object.__new__() takes exactly one argument
这个错误,而如果将__init__
的参数也传到__new__
中去,则不会有这种问题。比如这样:
class Singleton1:
def __init__(self, a, b, c, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
self.b = b
self.c = c
def __new__(cls, a, b, c, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton1, cls).__new__(cls, *args, **kwargs)
return cls._instance
single = Singleton1(2, {1, 2}, {1: 2})
但是上面好像也说的过去,我又进行了下一步操作,即拷贝单例模式
from copy import deepcopy
class Singleton1:
def __init__(self, a, b, c, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
self.b = b
self.c = c
def __new__(cls, a, b, c, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton1, cls).__new__(cls, *args, **kwargs)
return cls._instance
single = Singleton1(2, {1, 2}, {1: 2})
f = deepcopy(single)
结果是报错,信息如下,这里只选取了异常栈信息中真正错误的原因:
File "C:\Program Files\Python37\lib\copyreg.py", line 88, in __newobj__
return cls.__new__(cls, *args)
TypeError: __new__() missing 3 required positional arguments: 'a', 'b', and 'c'
原来是deepcopy
调用了cls.__new(cls, *args)
造成的错误,而我们这里的单例模式导致了这里参数传递的不统一。所以还是建议使用文章开头的那种方式创建单例模式。我们来看看使用开头那种方法创建单例模式并用deepcopy
的效果:
from copy import deepcopy
class Singleton1:
def __init__(self, a, b, c, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
self.b = b
self.c = c
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton1, cls).__new__(cls)
return cls._instance
single = Singleton1(2, {1, 2}, {1: 2})
copyed = deepcopy(single)
print(id(copyed) == id(single)) # True
不过话说回来,如果不希望单例模式创建的对象被deepcopy
,还是使用后来的方式吧。
有趣的事
class Singleton1:
def __init__(self, a, b, c, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = a
self.b = b
self.c = c
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = Singleton1(1, 2, 3)
return cls._instance
single = Singleton1(2, {1, 2}, {1: 2})
错误是RecursionError: maximum recursion depth exceeded