Python 在类内调用super()';classmethod以获取元类方法
就在我以为我懂元类的时候 免责声明:在发布之前,我已经四处寻找答案,但我找到的大多数答案都是关于调用Python 在类内调用super()';classmethod以获取元类方法,python,python-3.x,metaclass,Python,Python 3.x,Metaclass,就在我以为我懂元类的时候 免责声明:在发布之前,我已经四处寻找答案,但我找到的大多数答案都是关于调用super()以获取MRO中的另一个@classmethod(不涉及元类),或者,令人惊讶的是,他们中的很多人都在尝试在元类中做一些事情。\uuuu new\uuuu或元类。\uuuu call\uuuuu这意味着类还没有完全创建。我非常肯定(比方说97%)这不是这些问题之一 环境:Python 3.7.2 问题: 我有一个元类FooMeta,它定义了一个方法get_-foo(cls),一个类
super()
以获取MRO中的另一个@classmethod
(不涉及元类),或者,令人惊讶的是,他们中的很多人都在尝试在元类中做一些事情。\uuuu new\uuuu
或元类。\uuuu call\uuuuu
这意味着类还没有完全创建。我非常肯定(比方说97%)这不是这些问题之一
环境:Python 3.7.2
问题: 我有一个元类
FooMeta
,它定义了一个方法get_-foo(cls)
,一个类foo
,它是从该元类(因此是FooMeta的一个实例)构建的,并且有一个@classmethod
get_-bar(cls)
。然后是从Foo
继承的另一个类Foo2
。在Foo2
中,我通过声明它是@classmethod
并调用super()
来子类get\u foo
。这失败得很惨
i、 e.使用此代码
class FooMeta(类型):
def get_foo(cls):
返回5
类Foo(元类=FooMeta):
@类方法
def获取工具栏(cls):
返回3
打印(Foo.get\u Foo)
# >>>
打印(Foo.get\u栏)
# >>>
第二类(Foo):
@类方法
def get_foo(cls):
打印(cls.\uuuu mro\uuuuuu)
# >>> (, )
return super().get_foo()
@类方法
def获取工具栏(cls):
返回super().get_bar()
打印(Foo2().get_bar())
# >>> 3
打印(Foo2().get\u foo())
#>>>AttributeError:“super”对象没有属性“get\u foo”
问题:
既然我的类是元类的一个实例,并且已经验证了类Foo
上存在两个类方法,为什么对super()的两个调用都不能在Foo2
内部工作呢我对元类或阻止我发现这些结果符合逻辑的super()
有什么不理解的地方?
编辑:进一步的测试表明Foo2
上的方法作为类方法或实例方法不会改变结果
编辑2:多亏了@chepner的回答,我认为问题在于super()
返回了一个表示Foo
(这是通过super()。\uuuu this class\uuuu
验证的)的超级对象,我希望super()。get\ufoo()
在幕后表现(甚至可能调用)get\u attr(Foo,'get\ufoo')
。看来不是。。。我仍然想知道为什么,但事情越来越清楚了:)注1
我想说清楚:
Foo
不继承自FooMeta
FooMeta
不是Foo
super
将不起作用
附注2
现在,注意事项(1)已经不存在了,如果您想从元类实例的方法内部访问元类方法,您可以这样做:
class FooMeta(type):
_foo = 5
def get_foo(cls):
print("`get_foo` from `FooMeta` was called!")
class Foo(metaclass=FooMeta):
@classmethod
def bar(Foo):
FooMeta = type(Foo)
FooMeta_dot_getfoo = FooMeta.get_foo
FooMeta_dot_getfoo(Foo)
def baz(self):
Foo = type(self)
FooMeta = type(Foo)
FooMeta_dot_getfoo = FooMeta.get_foo
FooMeta_dot_getfoo(Foo)
Foo.bar()
foo = Foo()
foo.baz()
def __setattr__(self, attr_name, attr_val):
if hasattr(type(self), attr_name):
setattr(type(self), attr_name, attr_val)
else:
super_class = inspect.getmro(type(self))[1]
super_class.__setattr__(self, attr_name, attr_val)
输出为:
`get_foo` from `FooMeta` was called!
`get_foo` from `FooMeta` was called!
附注3
如果您有一个与元类中的方法同名的classmethod,为什么元类方法没有被调用?考虑下面的代码:
class FooMeta(type):
def get_foo(cls):
print("META!")
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(cls):
print("NOT META!")
Foo.get_foo()
输出是而不是META代码>在以下讨论中,假设:
foo
是foo
Foo
是FooMeta
在本文中,我将第一次使用伪代码,而不是python。不要尝试运行以下命令<代码>\uuuu getattribute\uuuu
排序如下所示:
class FooMeta(type):
def get_foo(Foo):
print("META!")
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(Foo):
print("NOT META!")
def __getattribute__(foo, the string "get_foo"):
try:
attribute = "get_foo" from instance foo
except AttributeError:
attribute = "get_foo" from class Foo
# Begin code for handling "descriptors"
if hasattr(attribute, '__get__'):
attr = attribute.__get__(None, Foo)
# End code for handling "descriptors"
return attribute
foo = Foo()
foo.get_foo() # prints "NOT META!"
get_foo = Foo.__getattribute__(foo, "get_foo")
get_foo.__call__()
实际上,您可以忽略上面的内容,“用于处理“描述符的代码”
”,我只是为了完整起见才包含它
请注意,\uuuuu getattribute\uuuuu
没有任何地方会说“从元类获取get\u foo
”
首先,我们尝试从实例中获取get_foo
。可能get\u foo
是一个成员变量。可能一个实例有get\u foo=1
,另一个实例有get\u foo=5
计算机不知道。电脑很笨
计算机意识到实例没有名为get\u foo
的成员变量。然后它说,“啊哈!我打赌get\u foo
属于这个类。”因此,它看起来在那里,你看,它在那里:foo
有一个名为get\u foo
的属性FooMeta
还有一个名为get\u foo
的属性,但谁会在乎呢
需要重点关注的是:
Foo
有一个名为get\u Foo
MetaFoo
有一个名为get\u foo
它们都具有名为get\u foo
的属性,但是foo
和MetaFoo
是不同的对象。这两个get\u foo
s并不是共享的。我可以有obj1.x=1
和obj2.x=99
。没问题
FooMeta
有自己的\uuu getattribute\uuu
方法。在我谈到Foo.\uuuuuGetAttribute\uuuuuuuuuu
之前,现在让我们谈谈元
class FooMeta(type):
def get_foo(Foo):
print("META!")
def __getattribute__(Foo, the string "get_foo"):
try: # LINE 1
attribute = "get_foo" from class Foo # LINE 2
except AttributeError: # LINE 3
attribute = "get_foo" from class FooMeta # LINE 4
# LINE 5
# Begin code for handling "descriptors"
if hasattr(attribute, '__get__'):
attr = attribute.__get__(None, Foo)
# End code for handling "descriptors"
return attribute
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(Foo):
print("NOT META!")
Foo.get_foo()
get_foo = FooMeta.__getattribute__(Foo, "get_foo")
get_foo.__call__()
事件顺序:
- 第1行和第2行发生
- 第3行、第4行和第5行不会发生
- 您可以忽略有关描述符的内容,因为在这个问题中,没有任何不同的
get\u foo
s具有\uu get\uu
方法
好了!为什么只有第1行和第2行?因为你做了一个@classmethod
很愚蠢!我们检查Foo
,看看它是否有get\u Foo
,它确实有!如果我们先找到实例属性,为什么还要检查类属性?我们总是检查属性是否属于实例(<
14
29
1
29
29
29
def __setattr__(self, attr_name, attr_val):
if hasattr(type(self), attr_name):
setattr(type(self), attr_name, attr_val)
else:
super_class = inspect.getmro(type(self))[1]
super_class.__setattr__(self, attr_name, attr_val)
29
29
29
class Foo:
@classmethod
def funky(cls):
pass
def funky(cls)
pass
Funky = classmethod (funky)
def funky(cls):
pass
funky = lambda self, *args, **kwargs: funky(type(self), *args, **kwargs)
>>> 'get_foo' in Foo.__dict__
False
>>> 'get_foo' in type(Foo).__dict__
True
class A:
@classmethod
def f(cls):
return 1
class B(A):
pass
class C(A):
@classmethod
def f(cls):
return 2
class D(B, C):
@classmethod
def f(cls):
return super().f() + 1