代码之家  ›  专栏  ›  技术社区  ›  Arun Kaliraja Baskaran

元类可以有方法吗?

  •  2
  • Arun Kaliraja Baskaran  · 技术社区  · 7 年前

    我试图为我的项目实现单例类,同时在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)
    

    将这两个方法标记为类方法。。

    现在,当我试图给他们打电话时:

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

    3 回复  |  直到 6 年前
        1
  •  6
  •   artu-hnrq    3 年前

    python元类可以有方法吗?

    是的,正如第一个示例所示,它们可以有方法,并且可以在实现元类的类上调用。

    例如在python-3中。x元类 type mro

    >>> object.mro()
    [object]
    

    但您无法在实例上访问它们:

    >>> object().mro()
    AttributeError: 'object' object has no attribute 'mro'
    

    但当我这么做的时候 dir(someClass) 这两种方法不显示。

    dir 电话 type.__dir__ 这只是展示了有限数量的方法:

    方法之一:

    >>> 'mro' in dir(object)
    False
    

    然而 目录 __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']
    

    现在,当您调用时,类的这些方法是可见的 .

    向元类添加方法正确吗?

    这取决于语境。我认为可以将这些方法添加到元类中。然而,这些应该很少使用。

    classmethod . 否则,它将显示正确的类。我不明白这种行为,有人能解释一下吗?

    如果你仔细想想,这是显而易见的。 Singleton 是你的班级吗 someClass 当你成为一个 这个 cls 参数将为 独生子女 _instances someClass类 cls公司

    但这只是一个惯例,因为 self 是的实例的典型参数名称 class cls公司 实例的典型参数名称 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.
    

    因此,您将“类”添加到dict中,然后检查元类是否在dict中(而不是)。

    classmethods (除了那些应该/可能是classmethods的方法,例如。 __prepare__

        2
  •  4
  •   user2357112    7 年前


    dir doesn't show everything :

    dir() 主要是为了方便在交互提示下使用,它尝试提供一组有趣的名称,而不是提供一组严格或一致定义的名称,其详细行为可能会在不同版本中发生变化。例如 .


    正如你所看到的,当我将其装饰为classmethod时,类is print表示其为Singleton。

    classmethod ! 那个 Singleton 自身或子类 独生子女 独生子女 . 具有的类 独生子女 就像他们的元类一样 独生子女;事实上,他们是阶级不是一个理由,把 类方法 独生子女 的方法。

        3
  •  3
  •   Ignacio Vazquez-Abrams    7 年前