如何检索元类方法

rmo*_*hea 3 python metaclass

在Python中工作,如何通过实例化的类检索元类(metamethod)所拥有的方法?在以下场景中,它很简单 - 只需使用getattr或点符号:

*所有示例都使用版本安全 with_metaclass

class A(type):
        class A(type):
    """a metaclass"""

    def method(cls):
        m = "this is a metamethod of '%s'"
        print(m % cls.__name__)

class B(with_metaclass(A, object)):
    """a class"""
    pass

B.method()
# prints: "this is a metamethod of 'B'"
Run Code Online (Sandbox Code Playgroud)

然而,这是奇特的,因为'method'在任何地方都找不到dir(B).由于这个事实,重写这样的方法动态变得困难,因为元类不在查找链中super:

class A(type):
    """a metaclass"""

    def method(cls):
        m = "this is a metamethod of '%s'"
        print(m % cls.__name__)

class B(with_metaclass(A, object)):
    """a class"""

    @classmethod
    def method(cls):
        super(B, cls).method()

B.method()

# raises: "AttributeError: 'super' object has no attribute 'method'"
Run Code Online (Sandbox Code Playgroud)

那么,正确覆盖元方法的最简单方法是什么?我已经回答了这个问题,但期待任何建议或替代答案.在此先感谢您的回复.

jsb*_*eno 5

正如你所说的,并在实践中发现,A.method不是在B的查找链-类和元类的关系是不是遗传的人-这是"实例"作为一类是元类的实例之一.

Python是一种很好的语言,它以预期的方式运行,很少有惊喜 - 在这种情况下它是相同的:如果我们处理'普通'对象,你的情况就像拥有一个类的实例 一样.并且将存在于- 对于为这样的实例定义的方法放置的"超级"调用永远不会到达- 它实际上会产生错误.作为一个类对象,在B的方法内部是有意义的 - 但它将搜索B的类链(在这种情况下) - 这就是你所击中的.BAB.methodB.__dict__A.methodBsuper__dict____mro__(object,)

这种情况不应该经常发生,我甚至认为它根本不会发生; 语义上很难在一个方法中存在任何意义,这个方法既可以作为元类方法,也可以作为类本身的方法.此外,如果method未在B注释中重新定义,则它甚至不会从B的实例中可见(也不可调用).

也许你的设计应该:

一个.有一个基类Base,使用元类A,其限定method具有它在限定的,而不是A-然后定义class B(Base):代替

湾 或者让元类在它创建的每个元素中A注入,并使用其中的代码或方法 - 沿:methodclass__init____new__

def method(cls):
    m = "this is an injected method of '%s'"
    print(m % cls.__name__)

class A(type):
    def __init__(cls, name, bases, dct):
        dct['method'] = classmethod(method)
Run Code Online (Sandbox Code Playgroud)

这将是我的首选方法 - 但它不允许method在使用此元类的类中覆盖它- 如果没有一些额外的逻辑,上面的方法将覆盖method正文中的任何此类显式.更简单的事情是Base如上所述拥有一个基类,或者base_method在最终类中注入一个具有不同名称的方法,并在任何覆盖中对其进行硬编码调用 method:

class B(metaclass=A):
   @classmethod
   def method(cls):
        cls.base_method()
        ...
Run Code Online (Sandbox Code Playgroud)

(if在元类上使用额外的,__init__以便默认method为别名base_method)

你真正要求的是从这里开始

现在,如果你真的有一个用于从类中调用的元类中的方法的用例,那么"一个明显的"方法就是简单地对调用进行硬编码,因为它是在存在之前完成的. super

你可以这样做:

class B(metaclass=A):
   @classmethod
   def method(cls):
        A.method(cls)
        ...
Run Code Online (Sandbox Code Playgroud)

哪个不那么动态,但不那么神奇,更具可读性 - 或者:

class B(metaclass=A):
   @classmethod
   def method(cls):
        cls.__class__.method(cls)
        ...
Run Code Online (Sandbox Code Playgroud)

哪一个更动态的(该__class__属性适用于cls只是像它会在案件工作B是的只是一些实例A像我在第二段例如:B.__class__ is A)

在这两种情况下,您都可以method通过简单的方法防止调用元类中的不存在if hasattr(cls.__class__, "method"): ...