【问题标题】:Instantiating a just-created class in a metaclass results in a RuntimeError ("super(): empty __class__ cell")在元类中实例化刚刚创建的类会导致 RuntimeError ("super(): empty __class__ cell")
【发布时间】:2015-09-09 06:31:09
【问题描述】:

所以我有这个元类,我想用它来自动注册新组件,即某些基本组件类的子类。当注册一个新组件时,它的 instance 应该被传递给处理它的 register_component() 函数。

元类代码(精简版):

class AutoRegisteredMeta(type):
    def __new__(metacls, name, bases, attrs):

        # ... (omitted) check if "name" has already been registered ...

        new_class = super().__new__(metacls, name, bases, attrs)
        register_component(name, new_class())  # RuntimeError(super(): empty __class__ cell)
        return new_class

问题是调用new_class() 会导致错误——但并非针对所有类。经过一些实验,我意识到只有在子类在其自己的 __init__() 方法中调用 super().__init__() 时才会发生这种情况。

示例组件和基类:

class BaseComponent(metaclass=AutoRegisteredMeta):
    def __init__(self):
        # do some work here ...

class ComponentFoo(BaseComponent):
    def __init__(self):
        super().__init__()  # <--- RuntimeError occurs here
        self.foo = 'bar'

我在这里做错了什么?阅读this 我发现我可能不应该在元类__new__()__init__() 中进行实例化,对吧?这可能会以某种方式规避吗?

另外,用外行的话做一些解释会很好,我不太了解 CPython 实现的内部结构。

提前致谢!

(FWIW,我使用 Python 3.3.6,Ubuntu)


编辑:我正在添加请求的最小示例,您可以直接运行它并自己查看错误。

#!/usr/bin/env python3


class AutoRegisteredMeta(type):
    def __new__(metacls, name, bases, attrs):
        new_class = super().__new__(metacls, name, bases, attrs)
        new_class()  # <--- RuntimeError can occur here
        return new_class


class BaseComponent(metaclass=AutoRegisteredMeta):
    def __init__(self):
        print("BaseComponent __init__()")


class GoodComponent(BaseComponent):
    def __init__(self):
        print("GoodComponent __init__()")


class BadComponent(BaseComponent):
    def __init__(self):
        print("BadComponent __init__()")
        super().__init__()  # <--- RuntimeError occurs because of this

【问题讨论】:

  • 您的注册代码实际上在元类完成之前实例化了该类的一个实例。 register_component(name, new_class) 可能会更好。
  • 您能否将其重组为实际运行的minimal example(包括register_componentBaseComponent.__init__、...)?
  • @CharlieClark 是的,我知道,这正是问题所在。不过,这让我想到,注册类本身而不是它的实例会更好,这正是您在评论中所建议的。注册组件实例与系统其余部分当前所做的一致,但现在我有一个很好的论据来重构它。 :)
  • 您可能还会查看某种类装饰器/扫描系统以进行注册。想想为什么这些方法比你的代码更受欢迎。

标签: python metaclass


【解决方案1】:

也许这行得通:

#!/usr/bin/env python3


class AutoRegisteredMeta(type):
    def __new__(metacls, name, bases, attrs):
        new_class = super().__new__(metacls, name, bases, attrs)
        new_class()
        return new_class


class BaseComponent(metaclass=AutoRegisteredMeta):
    def __init__(self):
        print("BaseComponent __init__()")


class GoodComponent(BaseComponent):
    def __init__(self):
        print("GoodComponent __init__()")


class BadComponent(BaseComponent):
    def __init__(self):
        print("BadComponent __init__()")
        super(self.__class__, self).__init__()

【讨论】:

  • 我试过了,确实有效,谢谢。向super() 添加显式参数,即self.__class__self,使问题消失。接受这个答案。顺便说一句,您可能希望删除源中的最后一条评论,因为它不再适用。
  • 我刚刚研究过
  • 顺便说一句,不要使用super(self.__class__, self).__init__(),因为它会导致无限循环——self 可能是子类的一个实例,像这样使用super() 实际上可以调用__init__() 方法包含super() 声明,一遍又一遍。
猜你喜欢
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-21
  • 2020-04-17
  • 1970-01-01
  • 1970-01-01
  • 2019-03-13
相关资源
最近更新 更多