gpt4 book ai didi

python - 在 Python 中创建 Singleton 类并计算实例数

转载 作者:行者123 更新时间:2023-11-28 22:15:09 27 4
gpt4 key购买 nike

我试图了解如何在 Python 中创建单例类。以下是我的尝试

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

理想情况下,__new__() 使用对象实例调用 __init__(),但在上述情况下,当我尝试创建第二个对象时 b, __new__ 不会被调用,因为 MyClass 的一个实例已经存在那么为什么打印语句打印 num_of_instances 打印 2?

最佳答案

__new__ 在每次 MyClass(...) 调用时被调用。如果它没有被调用,它将无法返回单例实例。

__new__ 方法返回一个对象时,该对象是传递给 __new__(或子类)的 cls 参数的一个实例, 然后 __init__ 方法也被调用。

因此,对于每个 MyClass(...) 调用,都会调用 __new____new__ 方法总是返回当前类的实例,因此每次都会调用 __init__。在这里,每次都是同一个实例并不重要。

来自__new__ method documentation :

If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__().

如果在方法中添加一些 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)

您不能阻止自动 __init__ 调用,至少不能不覆盖其他内容。如果你想避免 __init__ 每次都被调用,你有一些选择:

您不必在子类上使用 __init__ 方法。你可以发明你自己的机制,__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

或您的 __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

__new____init__逻辑在type.__call__中实现;你可以创建一个 metaclass覆盖那个逻辑。虽然您可以简单地只调用 __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

然后不将其用作基类,而是与 metaclass=... 一起使用。如果这样更容易,您可以创建一个空基类:

class Singleton(metaclass=SingletonMeta):
pass

class MyClass(Singleton):
# ...

上面的代码将在类上调用 __new__,然后可以选择在结果实例上调用 __init__,仅一次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

关于python - 在 Python 中创建 Singleton 类并计算实例数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52968491/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com