Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/jsf/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在类内调用super()';classmethod以获取元类方法_Python_Python 3.x_Metaclass - Fatal编程技术网

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