我正在尝试为我的项目实现单例类,并在 StackOverflow 中发表了一篇有趣的帖子
Creating a singleton in Python
我决定采用提到的元类方法..
现在..我尝试添加一个方法来获取和清除实例(以防用户想要摆脱当前实例并创建一个新实例..):
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def getInstance(cls):
print("Class is {}".format(cls.__name__))
if not cls in cls._instances:
raise LookupError("No instance of the class {cls} create yet.".format(cls.__name__))
return cls._instances[cls]
def clearInstance(cls):
cls._instances.pop(cls, None)
class someClass(metaclass=Singleton):
def __init__(self,val):
self.value = val
我能够成功地创建对象..
In [9]: sc = someClass(1)
In [10]: sc.value
Out[10]: 1
但是当我执行 dir(someClass) 时,这两个方法没有显示:
In [14]: dir(someClass)
Out[14]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
尽管如此,我还是可以调用这些方法..
In [13]: someClass.getInstance()
Class is someClass
Out[13]: <__main__.someClass at 0x7f728b180860>
在我在网上看到的关于元类的所有示例中,我看到了 __new__、__init__ 和 __call__ 方法的实现,但我没有看到任何其他方法添加的方法。向元类中添加方法是否正确?
我还尝试了上述元类代码的一个小变体:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
@classmethod
def getInstance(cls):
print("Class is {}".format(cls.__name__))
if not cls in cls._instances:
raise LookupError("No instance of the class {cls} create yet.".format(cls.__name__))
return cls._instances[cls]
@classmethod
def clearInstance(cls):
cls._instances.pop(cls, None)
将这 2 个方法标记为类方法..
现在,当我尝试调用它们时:
In [2]: sc = someClass(1)
In [3]: someClass.getInstance()
Class is Singleton
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-3-c83fe01aa254> in <module>()
----> 1 someClass.getInstance()
<ipython-input-1-9efb6548d92d> in getInstance(cls)
12
13 if not cls in cls._instances:
---> 14 raise LookupError("No instance of the class {cls} create yet.".format(cls.__name__))
15
16 return cls._instances[cls]
KeyError: 'cls'
如您所见,当我将它装饰为 classmethod 时,class is print 显示其 Singleton。否则它显示正确的类。我不明白这种行为,有人可以解释一下吗?
请您参考如下方法:
Can python metaclasses have methods?
是的,正如您的第一个示例所示,它们可以具有方法,并且可以在实现您的元类的类上调用它们。
例如在 python-3.x 中,元类 type 实现了 mro 属性:
>>> object.mro()
[object]
但是你不能在实例上访问它们:
>>> object().mro()
AttributeError: 'object' object has no attribute 'mro'
But when I do
dir(someClass)the 2 methods are not displayed.
dir调用 type.__dir__ 并且只显示了有限数量的方法:
If the object is a type or class object, the list contains the names of its attributes, and recursively of the attributes of its bases.
这里没有提到元类的方法。那是因为它们在默认情况下是隐藏的。
这就是为什么您也看不到 mro 方法的原因:
>>> 'mro' in dir(object)
False
但是 dir 允许自定义您看到的内容,因此您可以简单地覆盖它。因为 __dir__ 是在“实例的类”上调用的,而你的“元类是你的类的类型”,你必须在你的元类上实现它:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def getInstance(cls):
print("Class is {}".format(cls.__name__))
if not cls in cls._instances:
raise LookupError("No instance of the class {cls} create yet.".format(cls.__name__))
return cls._instances[cls]
def clearInstance(cls):
cls._instances.pop(cls, None)
def __dir__(self):
normal_dir = type.__dir__(self)
# Also include all methods that don't start with an underscore and
# not "mro".
normal_dir.extend([
f for f in dir(type(self))
if not f.startswith('_')
and f != 'mro'
])
return normal_dir
class someClass(metaclass=Singleton):
def __init__(self,val):
self.value = val
>>> dir(someClass)
[..., 'clearInstance', 'getInstance']
现在,当您调用 dir 时,您的类的这些方法是可见的。
Is it correct to add methods to the metaclass?
这在一定程度上取决于上下文。我会说将方法添加到元类中很好。但是,这些应该很少使用。
As you can see the class is print says its Singleton when I decorate it as
classmethod. Otherwise it shows the correct class. I don't understand this behavior, can someone explain?
仔细想想就明白了。 Singleton 是您的 someClass 的类,当您将其设为 classmethod 时,cls 参数将为 单例。但是,已添加到 _instances 的类是 someClass。我可以看到它来自哪里。您的所有方法都采用 cls 参数。这可能让您相信它们“像”类方法(它们在某种程度上是,但不是元类而是实现元类的类!)。
但这只是一个约定,因为 self 是 class 实例的典型参数名称,cls 也是典型的元类 实例的参数名称。当元类上有类方法时,第一个参数可能应该称为 metacls。还解决了 str.format 的一个小问题(这是它抛出 KeyError 而不是 LookupError 的原因):
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
print(cls._instances) # print the dict after creating an instance
return cls._instances[cls]
@classmethod
def getInstance(metacls):
print("Class is {}".format(metacls))
if not metacls in metacls._instances:
raise LookupError("No instance of the class {0} create yet.".format(metacls.__name__))
return metacls._instances[metacls]
@classmethod
def clearInstance(metacls):
metacls._instances.pop(metacls, None)
class someClass(metaclass=Singleton):
def __init__(self,val):
self.value = val
>>> sc = someClass(1)
{<class '__main__.someClass'>: <__main__.someClass object at 0x00000235844F8CF8>}
>>> someClass.getInstance()
Class is <class '__main__.Singleton'>
LookupError: No instance of the class Singleton create yet.
因此,您将“类”添加到字典中,然后检查元类是否在字典中(事实并非如此)。
通常在元类上自定义 classmethods(除了那些应该/可能是 classmethods 例如 __prepare__)没有多大意义,因为你很少需要类的类型您的实例。
