在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)
那么,正确覆盖元方法的最简单方法是什么?我已经回答了这个问题,但期待任何建议或替代答案.在此先感谢您的回复.
正如你所说的,并在实践中发现,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"): ...