[英]Creating Singleton class in Python and counting the number of instances
I was trying to understand how to create a Singleton class in Python. 我试图了解如何在Python中创建Singleton类。 Below is how i attempted 以下是我的尝试
class Singleton(object):
_instance = None
def __new__(class_, *args, **kwargs):
if not isinstance(class_._instance, class_):
class_._instance = object.__new__(class_)
return class_._instance
class MyClass(Singleton):
num_of_instances = 0
def __init__(self, real = 5, imaginary = 6):
self.real = real
self.imaginary = imaginary
MyClass.num_of_instances += 1
a = MyClass(10, 20)
print(a.real)
print(a.imaginary)
b = MyClass()
print(MyClass.num_of_instances) # 2
Ideally __new__()
calls __init__()
with the object instance, but in the above case when I am trying to create second object b
, __new__
won't be called because an instance of MyClass
already exits then why does the print statement printing num_of_instances
print 2
? 理想情况下, __new__()
使用对象实例调用__init__()
,但是在上述情况下,当我尝试创建第二个对象b
,将不会调用__new__
因为MyClass
实例已经存在,那么为什么打印语句print num_of_instances
print 2
?
__new__
is called for every MyClass(...)
call . 每次MyClass(...)
调用都会调用 __new__
。 If it didn't get called, it would not be able to return the singleton instance. 如果没有被调用,它将无法返回单例实例。
And when the __new__
method returns an object and that object is an instance of the cls
argument passed to __new__
(or a subclass), then the __init__
method is also called. 当__new__
方法返回一个对象并且该对象是传递给__new__
(或子类)的cls
参数的实例时,还将调用__init__
方法。
So, for each MyClass(...)
call, __new__
is called. 因此,对于每个MyClass(...)
调用, __new__
调用__new__
。 The __new__
method always returns an instance of the current class, so __init__
is called, every time. __new__
方法始终返回当前类的实例,因此每次都调用__init__
。 It doesn't matter here that it is the same instance each time. 没关系,每次都是同一实例。
From the __new__
method documentation : 从__new__
方法文档中 :
If
__new__()
returns an instance ofcls
, then the new instance's__init__()
method will be invoked like__init__(self[, ...])
, whereself
is the new instance and the remaining arguments are the same as were passed to__new__()
. 如果__new__()
返回cls
的实例,则将调用新实例的__init__()
方法,就像__init__(self[, ...])
,其中self
是新实例,其余参数与传递给__new__()
。
You can see this happen if you add some print()
calls in the methods: 如果在方法中添加一些print()
调用,则可以看到这种情况:
>>> class Singleton(object):
... _instance = None
... def __new__(class_, *args, **kwargs):
... print(f'Calling {class_!r}(*{args!r}, **{kwargs!r})')
... if not isinstance(class_._instance, class_):
... print(f'Creating the singleton instance for {class_!r}')
... class_._instance = object.__new__(class_)
... return class_._instance
...
>>> class MyClass(Singleton):
... num_of_instances = 0
... def __init__(self, real=5, imaginary=6):
... print(f'Calling {type(self)!r}.__init__(self, real={real!r}, imaginary={imaginary!r})')
... self.real = real
... self.imaginary = imaginary
... MyClass.num_of_instances += 1
...
>>> a = MyClass(10, 20)
Calling <class '__main__.MyClass'>(*(10, 20), **{})
Creating the singleton instance for <class '__main__.MyClass'>
Calling <class '__main__.MyClass'>.__init__(self, real=10, imaginary=20)
>>> b = MyClass()
Calling <class '__main__.MyClass'>(*(), **{})
Calling <class '__main__.MyClass'>.__init__(self, real=5, imaginary=6)
You can't prevent the automatic __init__
call, at least not without overriding something else. 您不能阻止自动__init__
调用,至少不能不重写其他内容。 If you want to avoid __init__
being called each time, you have some options: 如果要避免每次都调用__init__
,则有一些选择:
You don't have to use an __init__
method on the subclass. 您不必在子类上使用__init__
方法。 You could invent your own mechanism, __new__
could look for a __singleton_init__
method and call that: 您可以发明自己的机制, __new__
可以寻找__singleton_init__
方法并调用该方法:
class Singleton(object):
_instance = None
def __new__(class_, *args, **kwargs):
if not isinstance(class_._instance, class_):
class_._instance = object.__new__(class_)
if hasattr(class_._instance, '__singleton_init__'):
class_._instance.__singleton_init__(*args, **kwargs)`
return class_._instance
or your __init__
method could check if there are already attributes set in vars(self)
(or self.__dict__
) and just not set attributes again: 或者您的__init__
方法可以检查是否已在vars(self)
(或self.__dict__
)中设置了属性,而不再设置属性:
class MyClass(Singleton):
def __init__(self, real=5, imaginary=6):
if vars(self):
# we already set attributes on this instance before
return
self.real = real
self.imaginary = imaginary
The __new__
and __init__
logic is implemented in type.__call__
; __new__
和__init__
逻辑在type.__call__
; you could create a metaclass that overrides that logic. 您可以创建一个覆盖该逻辑的元类 。 While you could simply call __new__
only (and leave everything as is), it makes sense to make the metaclass responsible for handling the Singleton pattern: 虽然您可以只简单地调用__new__
(并将所有内容__new__
),但使元类负责处理Singleton模式是有意义的:
class SingletonMeta(type):
def __new__(mcls, *args, **kwargs):
cls = super().__new__(mcls, *args, **kwargs)
cls._instance = None
return cls
def __call__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
then use this not as a base class but with metaclass=...
. 然后将其用作metaclass=...
而不是基类。 You can create an empty base class if that's easier: 如果更简单,则可以创建一个空的基类:
class Singleton(metaclass=SingletonMeta):
pass
class MyClass(Singleton):
# ...
The above will call __new__
on the class, optionally followed by __init__
on the resulting instance, just once . 上面的代码将在类上调用__new__
,然后在生成的实例上调用__init__
,只需调用一次 。 The SingletonMeta.__call__
implementation then, forever after, returns the singleton instance without further calls: 然后, SingletonMeta.__call__
实现永远在不执行任何进一步调用的情况下返回单例实例:
>>> class SingletonMeta(type):
... def __new__(mcls, *args, **kwargs):
... cls = super().__new__(mcls, *args, **kwargs)
... cls._instance = None
... return cls
... def __call__(cls, *args, **kwargs):
... print(f'Calling {cls!r}(*{args!r}, **{kwargs!r})')
... if cls._instance is None:
... cls._instance = super().__call__(*args, **kwargs)
... return cls._instance
...
>>> class Singleton(metaclass=SingletonMeta):
... pass
...
>>> class MyClass(Singleton):
... def __init__(self, real=5, imaginary=6):
... print(f'Calling {type(self)!r}.__init__(self, real={real!r}, imaginary={imaginary!r})')
... self.real = real
... self.imaginary = imaginary
...
>>> a = MyClass(10, 20)
Calling <class '__main__.MyClass'>(*(10, 20), **{})
Calling <class '__main__.MyClass'>.__init__(self, real=10, imaginary=20)
>>> MyClass()
Calling <class '__main__.MyClass'>(*(), **{})
<__main__.MyClass object at 0x10bf33a58>
>>> MyClass() is a
Calling <class '__main__.MyClass'>(*(), **{})
True
>>> MyClass().real
Calling <class '__main__.MyClass'>(*(), **{})
10
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.