Jat*_*aki 11 python super python-3.6 python-3.8
我的代码在 Python 3.6 中运行,但在 Python 3.8 中失败。似乎归结为调用super的子类typing.NamedTuple,如下所示:
<ipython-input-2-fea20b0178f3> in <module>
----> 1 class Test(typing.NamedTuple):
2 a: int
3 b: float
4 def __repr__(self):
5 return super(object, self).__repr__()
RuntimeError: __class__ not set defining 'Test' as <class '__main__.Test'>. Was __classcell__ propagated to type.__new__?
Run Code Online (Sandbox Code Playgroud)
In [3]: class Test(typing.NamedTuple):
...: a: int
...: b: float
...: #def __repr__(self):
...: # return super(object, self).__repr__()
...:
>>> # works
Run Code Online (Sandbox Code Playgroud)
此super(object, self).__repr__调用的目的是使用标准'<__main__.Test object at 0x7fa109953cf8>' __repr__而不是打印出元组元素的所有内容(默认情况下会发生这种情况)。有一些 关于导致类似错误的问题,super但它们:
super()typing.NamedTuple.我的问题是如何解决这个问题,同时保持与 Python 3.6 的向后兼容性(否则我只是使用@dataclasses.dataclass而不是继承自typing.NamedTuple)?
一个附带问题是,鉴于违规super调用位于尚未执行的方法中,这怎么会在定义时失败。例如:
In [3]: class Test(typing.NamedTuple):
...: a: int
...: b: float
...: def __repr__(self):
...: return foo
Run Code Online (Sandbox Code Playgroud)
有效(直到我们实际调用__repr__),即使foo是未定义的引用。就是super在这方面的神奇吗?
不幸的是,我不太熟悉 CPython 内部和类生成来说明它为什么失败,但是有这个 CPython 错误跟踪器问题似乎与Python 文档中的一些词有关
CPython 实现细节:在 CPython 3.6 及更高版本中,
__class__单元格作为__classcell__类命名空间中的条目传递给元类。如果存在,则必须将其传播到type.__new__调用才能正确初始化该类。否则将导致RuntimeErrorPython 3.8中的 a 。
所以可能在实际namedtuple创建过程中的某个地方,我们有一个type.__new__没有__classcell__传播的调用,但我不知道是不是这种情况。
但是这种特殊情况似乎可以通过不使用super()call 并明确地说“我们需要拥有类的__repr__方法object”来解决,例如
class Test(typing.NamedTuple):
a: int
b: float
__repr__ = object.__repr__
Run Code Online (Sandbox Code Playgroud)
我在另一个问题(我刚刚更新)中略有错误。显然,这种行为在的两种情况下都有体现super。事后看来,我应该对此进行测试。
这里发生的情况是元类NamedTupleMeta确实没有传递__classcell__到,type.__new__因为它动态创建一个命名元组并返回它。实际上,在 Python 3.6 和 3.7 中(这仍然是DeprecationWarning),__classcell__泄漏到类字典中,因为它没有被 删除NamedTupleMeta.__new__。
class Test(NamedTuple):
a: int
b: float
def __repr__(self):
return super().__repr__()
# isn't removed by NamedTupleMeta
Test.__classcell__
<cell at 0x7f956562f618: type object at 0x5629b8a2a708>
Run Code Online (Sandbox Code Playgroud)
按照 Azat 的建议直接使用object.__repr__就可以了。
这怎么会在定义时失败
同样的方式,以下也失败:
class Foo(metaclass=1): pass
Run Code Online (Sandbox Code Playgroud)
在构造类时会执行许多检查。其中,检查元类是否已传递__classcell__给type_new.
| 归档时间: |
|
| 查看次数: |
588 次 |
| 最近记录: |