Python 3.6 中结合 ABCMeta 和 __init_subclass__ 时出现 TypeError。

16

我正在尝试使用Python 3.6的新 __init_subclass__功能 (PEP 487) 与abc模块一起使用,但它似乎不起作用。以下是代码:

from abc import ABCMeta
class Initifier:
    def __init_subclass__(cls, x=None, **kwargs):
        super().__init_subclass__(**kwargs)
        print('got x', x)

class Abstracted(metaclass=ABCMeta):
    pass

class Thingy(Abstracted, Initifier, x=1):
    pass

thingy = Thingy()

运行时产生以下结果:

Traceback (most recent call last):
  File "<filename>", line 10, in <module>
    class Thingy(Abstracted, Initifier, x=1):
TypeError: __new__() got an unexpected keyword argument 'x'

如果抽象类没有使用ABCMeta元类,一切都正常工作。

这个错误非常强大,例如,以下代码仍然会因为类似的类型错误而失败(可能是因为元类的__new__在类实例化时运行,而父类的__new__直到对象实例化时才运行)。

from abc import ABCMeta

class Initifier:
    def __new__(cls, name, bases, dct, x=None, **kwargs):
        return super().__new__(cls, name, bases, dct, **kwargs)

    def __init_subclass__(cls, x=None, **kwargs):
        super().__init_subclass__(**kwargs)
        print('got x', x)

class Abstracted(metaclass=ABCMeta):
    pass

class Thingy(Initifier, Abstracted, x=1):
    pass

thingy = Thingy()

有人能确认这是否是Python 3.6中abc模块和/或__init_subclass__实现中的一个错误吗?(我可能在错误地使用__init_subclass__。)有人有解决方法吗?


6
在新的 __init_subclass__ 设计中有一个有趣的交互。现在存在的几乎每个元类都应该将意外的关键字参数传递给 super().__new__,以便 type.__new__ 可以将它们传递给 __init_subclass__,但是ABCMeta和可能还有许多其他元类没有这样做。 - user2357112
@RickTeachey 不幸的是,它无论继承顺序如何都会出现相同的 TypeError 错误。 - So8res
1
哎呀,那太糟糕了。 - Rick
如果您反转顺序并在Initifier中添加对__new__的调用,并在其中处理x参数,会怎样呢? - Rick
1
不用在意,这并不奇怪,因为异常发生在类的实例化过程中,而不是类实例化后。 - Rick
显示剩余3条评论
1个回答

12

abc.ABCMeta中存在一个错误,这是由于__init_subclass__设计上的一处缺陷导致的。我建议您进行报告。

目前几乎所有现有的元类都应该将意外的关键字参数传递给super().__new__,以便type.__new__可以将它们传递给__init_subclass__,但是abc.ABCMeta和可能还有其他许多元类尚未这样做。abc.ABCMeta.__new__无法处理x关键字参数而不是传递它,导致您看到的异常。

如果要使用__init_subclass__关键字参数,请确保所使用的元类已更新以适应新的设计。否则将无法正常工作,需要等待您使用的元类被修补程序修复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接