我在网上看到了一些关于如何利用python元类将类创建为Singleton的材料。代码片段大致如下:
class SingletonMetaClass(type):
_instance = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instance:
# usually a lock is used here, but omitted for simplicity
cls._instance[cls] = super().__call__(*args, **kwargs)
return cls._instance[cls]
class SingletonBaseClass(object, metaclass=SingletonMetaClass):
def __init__():
pass
class SingletonDerivedClass(SingletonBaseClass):
def __init__():
pass
上面的片段非常适合我——SingletonDerivedClass
的所有实例都是相同的。然而,我发现奇怪的是,SingletonMetaClass
中的行是cls._instance[cls] = super().__call__(*args, **kwargs)
。显然,前者的cls
指的是SingletonMetaClass
本身,后者指的是子类,在这种情况下,它将是SingletonDerivedClass
,但在__call__
的签名中只有一个cls
,python解释器如何判断哪个cls
指的是什么?
如有任何回复,不胜感激!
cls
的所有用法都没有引用元类。它们引用元类的实例SingletonBaseClass
或SingletonDerivedClass
。
当cls
是SingletonBaseClass
时查找cls._instance
会找到SingletonMetaClass._instance
,因为SingletonBaseClass
是SingletonMetaClass
的实例,并且查找对象上的属性也会搜索其类,即使对象本身也是类。
"利用元类在python中创建singleton类">
不要。
求你了。
这定义了过度杀戮。
因此,在解释出了什么问题或您的疑问之前,以下是如何在Python中定义singleton,它只能是usd作为singleton,除非有人决定使用内省来";创建一个以上的单例"-在这一点上,这个人可能会滥用你现有的任何thr机制。
只需创建一个类,然后创建将成为您的单例实例。然后从命名空间中删除对该类的引用。如果有代码试图实例化应该是singleton的类,那么放置一个返回self
:的__call__
方法
class MySingleton():
...
def __call__(self):
return self
mysingleton = MySingleton()
del MySingleton
如果代码不希望实例化类,或者由于其他原因需要调用singleton:这个__call__
只是装饰性的。实际上,singleton实例可以与类具有相同的名称,甚至不需要del
语句。
现在,对于您在代码中感到困惑的部分:就元类中的任何方法而言,如果它是一个";实例";方法:即取";自我;作为第一个参数,第一个参数指的是一个类!(不是元类(。因此,在为元类编写方法时,我们通常将cls
作为参数名称。
换句话说,在您的__call__
的情况下;cls";是元类的实例:类本身。为__call__
中的参数使用名称cls
并不会神奇地使其接收到对编写该方法的类(元类本身(的引用。
如果给定您的类,您希望获得对元类的引用,只需像处理任何其他Python对象一样继续:使用type
调用或检查__class__
属性。
因此,如果你选择忽略我的建议,只为拥有一个singleton而拥有元类,那么你的代码的修复方法就是:
def __call__(cls, *args, **kwargs):
if cls not in cls._instance:
# usually a lock is used here, but omitted for simplicity
cls.__class__._instance[cls] = super().__call__(*args, **kwargs)
return cls._instance[cls]
如果您希望元类方法中的第一个参数是元类本身,@classmethod
装饰器会这样做(但这种修饰符只能应用于普通方法,而不是"dunder"特殊方法,如__call__
,该语言希望__call__
和其他人将该实例作为第一个参数(