元类可以有方法吗?

Aru*_*ran 2 python methods metaclass python-3.x

我试图为我的项目和 StackOverflow 中的一个有趣的帖子实现单例类

在 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
Run Code Online (Sandbox Code Playgroud)

我能够成功创建对象..

In [9]: sc = someClass(1)

In [10]: sc.value
Out[10]: 1
Run Code Online (Sandbox Code Playgroud)

但是当我这样做时dir(someClass),两种方法都没有显示:

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
Run Code Online (Sandbox Code Playgroud)

不过我可以调用这些方法..

In [9]: sc = someClass(1)

In [10]: sc.value
Out[10]: 1
Run Code Online (Sandbox Code Playgroud)

在所有关于元类的例子我在网上看到我看到的__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)
Run Code Online (Sandbox Code Playgroud)

将 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__']
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,当我将class isSingleton装饰为classmethod. 否则它会显示正确的类。我不明白这种行为,有人可以解释一下吗?

MSe*_*ert 6

python元类可以有方法吗?

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

例如在 python-3.x 中,元类type实现了mro属性:

>>> object.mro()
[object]
Run Code Online (Sandbox Code Playgroud)

但是你不能在实例上访问它们:

>>> object().mro()
AttributeError: 'object' object has no attribute 'mro'
Run Code Online (Sandbox Code Playgroud)

但是当我这样做时dir(someClass),两种方法都没有显示。

dir调用type.__dir__,并且只显示了有限数量的方法:

如果对象是类型或类对象,则列表包含其属性的名称,以及其基类属性的递归名称。

这里没有提到元类的方法。那是因为这些默认情况下是隐藏的。

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

>>> 'mro' in dir(object)
False
Run Code Online (Sandbox Code Playgroud)

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

>>> object.mro()
[object]
Run Code Online (Sandbox Code Playgroud)

现在,当您调用dir.

向元类添加方法是否正确?

这在一定程度上取决于上下文。我会说将方法添加到元类很好。但是,这些应该很少使用。

正如你所看到的,当我将它装饰为classmethod. 否则它会显示正确的类。我不明白这种行为,有人可以解释一下吗?

仔细想想就很明显了。Singleton是您的班级,someClass当您将其设为a 时classmethodcls参数将是Singleton. 然而已添加到类_instancessomeClass。我可以看到它来自哪里。你所有的方法都需要一个cls参数。这可能让你相信它们“像”类方法(它们在某种程度上,但不是元类,而是实现元类的类!)。

但这只是一个约定,因为 aself实例的典型参数名称是这样, a 实例的典型参数名称也是class如此。当您的元类上有 classmethods 时,可能应该调用第一个参数。还修复了一个小问题(这就是它抛出 a而不是 a的原因):clsmetaclassmetaclsstr.formatKeyErrorLookupError

>>> object().mro()
AttributeError: 'object' object has no attribute 'mro'
Run Code Online (Sandbox Code Playgroud)

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

通常classmethods__prepare__元类上的自定义(除了那些应该/可能是 classmethods 的,例如)没有多大意义,因为您很少需要实例的类的类型。


use*_*ica 4

python 元类可以有方法吗?

是的。


但是当我执行 dir(someClass) 时,这两种方法不会显示

与您可能相信的相反,它dir并没有显示一切

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


正如你所看到的,当我将它装饰为 classmethod 时,该类 print 说它是 Singleton。

不要用 来装饰它classmethod!这明确表示Singleton您希望该方法对自身或 的子类进行操作Singleton,而不是对 的实例进行操作Singleton。以元类为元类的类Singleton是单例的实例;它们是类这一事实并不是放置classmethodonSingleton方法的理由。

  • 虽然我同意他们可以拥有方法,但也有人问“向元类添加方法是否正确?” ——这个问题有点难回答。我绝对对你在这一点上的想法感兴趣:) (2认同)