为什么定义新类有时会调用该类继承的对象的 __init__() 函数?

Dav*_*ean 4 python inheritance metaclass python-3.x

我试图了解当您声明一个从 python 中的父类继承的新类时实际发生的情况。

\n

这是一个非常简单的代码片段:

\n
# inheritance.py\nclass Foo():\n    def __init__(self, *args, **kwargs):\n        print("Inside foo.__init__")\n        print(args)\n        print(kwargs)\n\n\nclass Bar(Foo):\n    pass\n\nprint("complete")\n
Run Code Online (Sandbox Code Playgroud)\n

如果我运行它,就没有错误,并且输出正如我所期望的那样。

\n
\xe2\x9d\xaf python inheritance.py                                                                                                                                                                               \ncomplete\n
Run Code Online (Sandbox Code Playgroud)\n

这是一个有明显错误的脚本,我继承自 的实例而Foo()不是类Foo

\n
# inheritance.py\nclass Foo():\n    def __init__(self, *args, **kwargs):\n        print("Inside foo.__init__")\n        print(f"{args=}")\n        print(f"{kwargs=}\\n")\n\nfoo = Foo()\nclass Bar(foo):            <---- This is wrong\n    pass\n\nprint("complete")\n
Run Code Online (Sandbox Code Playgroud)\n

这段代码运行时不会崩溃,但是我不明白为什么Foo.__init__()被调用两次

\n

这是输出:

\n
\xe2\x9d\xaf python inheritance.py\nInside foo.__init__        <--- this one I expected\nargs=()\nkwargs={}\n\nInside foo.__init__       <--- What is going on here...?\nargs=('Bar', (<__main__.Foo object at 0x10f190b10>,), {'__module__': '__main__', '__qualname__': 'Bar'})\nkwargs={}\n\ncomplete\n
Run Code Online (Sandbox Code Playgroud)\n

在第 8 行,我实例化了Foo()没有参数的实例,这正是我所期望的。然而,第 9 行Foo.__init__使用通常传递type()来生成新类的参数进行调用。

\n

我可以隐约看到发生了什么:class Bar(...)是生成新类的代码,因此在某些时候type("Bar", ...)需要调用但是:

\n
    \n
  1. 这实际上是如何发生的?
  2. \n
  3. 为什么从实例继承会Foo()Foo.__init__("Bar", <tuple>, <dict>)调用?
  4. \n
  5. 为什么不type("Bar", <tuple>, <dict>)调用呢?
  6. \n
\n

che*_*ner 6

Python 用于foo确定要用于Bar. 没有给出显式的元类,因此必须确定“最派生的元类”。基类的元类是它的类型;通常,这就是type它本身。但在这种情况下,唯一的基“类”的类型fooFoo,因此成为最派生的元类。所以,

class Bar(foo):
    pass
Run Code Online (Sandbox Code Playgroud)

被视为

class Bar(metaclass=Foo):
    pass
Run Code Online (Sandbox Code Playgroud)

这意味着它Bar是通过调用 Foo创建的:

Bar = Foo('Bar', (foo,), {})
Run Code Online (Sandbox Code Playgroud)

请注意,Bar现在是 的实例Foo,而不是类型。是的,一条class语句并不一定会创建一个类。

  • @DiMithras因为“type”本身会检查您声称是基类的东西实际上是类(或者至少是具有与“type”兼容的元类的东西)。使用“class Bar(foo, metaclass=type): pass”会出现相同的错误。 (2认同)