2017-08-30 70 views
0

我试图实现单班为我的项目和一个有趣的帖子在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 

我能够创建OBJ学分顺利..

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。否则它显示正确的类。我不明白这种行为,有人可以解释吗?

回答

2

python metaclasses是否有方法?

是的。


但是当我做目录(SomeClass的)2种方法不显示

相反,你可能会认为,dir doesn't show everything

因为dir()主要供应为了便于在交互式提示符下使用,它试图提供一组有趣的名称,而不是试图提供严格或一致定义的一组na mes,并且其详细行为可能会在各版本中发生变化。例如,当参数是类时,元类属性不在结果列表中。


正如你可以看到类是打印表示,其辛格尔顿当我装点它作为类方法。

不要用classmethod装饰它!那明确说你想要的方法操作Singleton本身或Singleton的子类,而不是Singleton的实例。具有Singleton作为其元类的类是实例单例;他们是班级的事实不是把classmethod放在Singleton的方法上的理由。

+1

虽然我同意他们可以有方法,但也有人问:“向元类添加方法是否正确?” - 这有点难以回答。我肯定会对你的想法在这一点上感兴趣:) – MSeifert

3

类是它们的元类的实例。正如类的实例没有将类的方法作为属性,但仍然可以调用它们一样,类没有将元类的方法作为属性。

2

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时,这个类是print的。否则它显示正确的类。我不明白这种行为,有人可以解释吗?

这很明显,如果你想一想。 Singleton是您的someClass的等级,当您将其设置为classmethod时,cls参数将为Singleton。但是,添加到_instances的类是someClass。我可以看到它的来源。您的所有方法都采用cls参数。这可能会让你相信他们“喜欢”类方法(它们以某种方式,但不是元类,而是实现元类的类)。

但它只是一个约定,因为,像self是一个class实例的典型参数的名字,所以是clsmetaclass实例的典型参数名称。当你在metaclass上使用类方法时,第一个参数应该叫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(除了那些应该/可能是类方法,例如__prepare__)对元类没有多大意义,因为你很少需要你的实例类的类型。

+0

0123我知道,而不是someClass我得到SingletonClass作为CLS的论点..但我不明白为什么我得到的CLS作为SingletonClass一旦我装饰类方法.. –

+0

@ArunKalirajaBaskaran我编辑答案a包含更多细节。如果有什么不清楚,请告诉我。通过回答这个问题,我学到了很多东西,并且由于某些方面对我来说也是新的,所以在文中可能会有点“跳跃”。 :) – MSeifert

相关问题