元类可以有方法吗?

Can metaclasses have methods?

我正在尝试为我的项目实现单例 classes,并在 Whosebug 中实现一个有趣的 post 同样

Creating a singleton in Python

我决定采用提到的元classes 方法..

现在..我尝试添加一个方法来获取和清除实例(以防用户想要摆脱当前实例并创建一个新实例..):

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>

在 metaclass 上的所有示例中,我在网上看到我看到 __new____init____call__ 方法已实现,但我没有看到添加任何其他方法.向元class添加方法是否正确?

我还尝试了上述元class代码的一个小变体:

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 个方法标记为 class 方法..

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

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 印刷品显示为 Singleton。否则它会显示正确的 class。我不明白这种行为,有人可以解释一下吗?

类 是它们的元classes 的实例。正如 classes 的实例没有 class 的方法作为属性但仍然可以调用它们一样,classes 没有 meta[=12= 的方法] 作为属性。

Can python metaclasses have methods?

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

例如在 python-3.x 中,metaclass 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.

此处未提及 metaclass 的方法。那是因为它们默认是隐藏的。

这就是为什么您也看不到 mro 方法的原因:

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

但是 dir 允许自定义您看到的内容,因此您可以简单地覆盖它。因为 __dir__ 是在“实例的 class” 上调用的,而你的“metaclass 是你的 class 的类型”,你必须在你的 metaclass:

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']

现在,您 class 的这些方法在您调用 dir 时可见。

Is it correct to add methods to the metaclass?

这在一定程度上取决于上下文。我会说将这些方法添加到 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 的 class,当您将其设为 classmethod 时,cls 参数将为 Singleton。但是 class 添加到 _instancessomeClass。我可以看到它来自哪里。您所有的方法都采用 cls 参数。这可能让你相信它们是“像”class方法(它们在某种程度上是,但不是元class而是实现元class的class ]!).

但这只是一个约定,因为 selfclass 实例的典型参数名称,cls 是 a 实例的典型参数名称metaclass。当你的 metaclass 上有 class 方法时,第一个参数可能应该被称为 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”添加到字典中,然后检查元class 是否在字典中(它不在)。

通常自定义 classmethods(除了那些 should/could 是 class 方法,例如 __prepare__)在 metaclass 上没有多大意义,因为您很少需要实例的 class 类型。

Can python metaclasses have methods?

是的。


But when I do dir(someClass) the 2 methods are not displayed

与您可能相信的相反,dir doesn't show everything:

Because dir() is supplied primarily as a convenience for use at an interactive prompt, it tries to supply an interesting set of names more than it tries to supply a rigorously or consistently defined set of names, and its detailed behavior may change across releases. For example, metaclass attributes are not in the result list when the argument is a class.


As you can see the class is print says its Singleton when I decorate it as classmethod.

不要用classmethod装饰它! 明确表示 您希望该方法在 Singleton 本身或 Singleton 的子 类 上运行,而不是 Singleton 的实例。 类 以 Singleton 作为它们的元类是单例的 实例 ;他们是 类 的事实并不是将 classmethod 放在 Singleton 的方法上的理由。