我试图为我的项目实现单例类,并在同一个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)
时,不会显示2种方法:
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'
正如您可以看到class is
打印在我将其Singleton
装饰为classmethod
时所说的this.state.data.length> 0
。否则它会显示正确的类。我不明白这种行为,有人可以解释一下吗?
答案 0 :(得分:3)
类是其元类的实例。就像类的实例没有类的方法作为属性但仍然可以调用它们一样,类没有元类的方法作为属性。
答案 1 :(得分:3)
python元类可以有方法吗?
是的,因为您的第一个示例显示他们可以拥有方法,并且可以在实现您的元类的类上调用它们。
例如在python-3.x中,元类type
实现了mro
属性:
>>> object.mro()
[object]
但你不能在实例上访问它们:
>>> object().mro()
AttributeError: 'object' object has no attribute 'mro'
但当我
dir(someClass)
时,不会显示2种方法。
dir
调用type.__dir__
并且只显示有限数量的方法:
如果对象是类型或类对象,则列表包含其属性的名称,并且递归地包含其基础的属性。
这里没有提到元类的方法。那是因为默认情况下它们是隐藏的。
这就是为什么你没有看到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
时,您的班级可以看到这些方法。
向元类添加方法是否正确?
这取决于上下文。我想说将方法添加到元类是很好的。但是,这些应该很少使用。
正如你所看到的那样,当我把它装饰成
classmethod
时,它就是它的Singleton。否则它会显示正确的类。我不明白这种行为,有人可以解释一下吗?
如果你考虑一下这很明显。 Singleton
是您someClass
的一个类,当您创建classmethod
时,cls
参数将为Singleton
。但是,已添加到_instances
的课程为someClass
。我可以看到它来自哪里。您的所有方法都采用cls
参数。这可能让你相信它们是“类似”类方法(它们在某种程度上,但不是元类,而是实现元类的类!)。
但它只是一种约定,因为self
是class
实例的典型参数名称,cls
是metaclass
实例的典型参数名称。 1}}。当你的元类上有类方法时,第一个参数应该被称为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.
所以你将“class”添加到dict中,然后检查元类是否在dict中(它不是)。
通常自定义classmethods
(除了那些应该/可能是类方法,例如__prepare__
)在元类上没有多大意义,因为你很少需要你的实例类的类型。
答案 2 :(得分:3)
python元类可以有方法吗?
是
但是当我做dir(someClass)时,没有显示2种方法
与您可能认为的相反,dir
doesn't show everything:
因为
dir()
主要是为了方便在交互式提示中使用而提供的,所以它试图提供一组有趣的名称,而不是尝试提供严格或一致定义的名称集,以及它的详细行为可能会在不同的版本例如,当参数是类时,元类属性不在结果列表中。
正如你所看到的那样,当我把它作为classmethod装饰时,它就是它的Singleton。
请勿使用classmethod
进行装饰! 明确指出您希望该方法在Singleton
本身或Singleton
的子类上运行,而不是Singleton
的实例。以Singleton
作为其元类的类是singleton的实例;它们是类的事实并不是classmethod
在Singleton
方法上的理由。