![](/img/trans.png)
[英]python dataclass `__init_subclass__` does not load the fields from subclass
[英]dataclasses.dataclass with __init_subclass__
我的困惑是数据类和dataclasses
之间的__init_subclass__
。
我正在尝试实现一个基本的 class ,它将完全继承自。 在此示例中, A
是基础 class。 通过阅读有关数据类的python文档,我了解到,简单地添加装饰器应该会自动为我创建一些特殊的 dunder 方法。 引用他们的文档:
例如,这段代码:
from dataclasses import dataclass
@dataclass
class InventoryItem:
"""Class for keeping track of an item in inventory."""
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
除其他外,将添加一个
__init__()
,如下所示:
def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
self.name = name
self.unit_price = unit_price
self.quantity_on_hand = quantity_on_hand
这是一个实例变量,不是吗? 从 classes docs中,它显示了一个玩具示例,读起来非常清晰。
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
我理解的一个主要差距是 - 它是实例变量还是 class 变量? 从我下面的测试来看,它是一个 class 变量,但从文档中,它显示了一个实例变量,因为它是最接近的实现。 可能我的大部分问题都在那里。 我还阅读了有关classes的 python 文档,这些文档不会 go 进入dataclasses
。
__init_subclass__
上看似有限的文档仍然存在问题,这在我的理解中产生了另一个差距。 我也在使用__init_subclass__
,以强制我的子类确实实例化了变量x
。
下面,我们有A
,它的实例变量x
设置为 None。 B
, C
和D
都是子类A
,以不同的方式(希望)确定实现细节。
B
继承自A
,将 class 变量设置为x
。 D
是一个数据类,它继承自A
,设置看起来像是x
的 class 变量。 但是,从上面给出的文档来看,似乎应该将D
的 class 变量x
创建为实例变量。 因此,当创建D
时,它应该首先调用__init_subclass__
,在那个 function 中,它会检查D
中是否存在x
- 据我了解,它不应该; 但是,代码通过了scot-free。 我相信D()
会将x
创建为实例变量,因为数据类文档显示这将为用户创建一个__init__
。
“除其他外,将添加...” <插入
__init__ code
>
我在这里一定是错的,但我正在努力把它放在一起。
import dataclasses
class A:
def __init__(self):
self.x = None
def __init_subclass__(cls):
if not getattr(cls, 'x') or not cls.x:
raise TypeError(
f'Cannot instantiate {cls.__name__}, as all subclasses of {cls.__base__.__name__} must set x.'
)
class B(A):
x = 'instantiated-in-b'
@dataclasses.dataclass
class D(A):
x : str = 'instantiated-in-d'
class C(A):
def __init__(self):
self.x = 'instantiated-in-c'
print('B', B())
print('D', D())
print('C', C())
根据我的期望,该代码正确地失败了C()
。 执行上述代码将成功D
,这对我来说并不计算。 根据我的理解(这是错误的),我定义了一个field
,这意味着数据类应该将我的dataclass
变量扩展为实例变量。 (前面的陈述很可能是我错的地方,但我找不到任何记录这种行为的东西。数据类实际上没有将 class 变量扩展为实例变量吗?从他们的文档中的视觉解释来看,它肯定是这样的。)来自数据类文档:
dataclass() 装饰器检查 class 以查找字段。 字段定义为具有类型注释的 class 变量。
因此 -为什么- 在创建实例D()
时 - 它会滑过其父A
的__init_subclass__
吗?
为冗长的帖子道歉,我一定错过了一些简单的东西,所以如果能指出我正确的方向,那就太好了。 蒂亚!
我刚刚从 CPython github 中找到了数据类的 实现。
相关文章:
初始化子类时调用__init_subclass__
。 不是在初始化子类的实例时 - 它在初始化子类本身时被调用。 您的异常发生在尝试创建C
class 时,而不是在尝试评估C()
时。
装饰器,例如@dataclass
,是一种后处理机制,而不是一种预处理机制。 class 装饰器采用已经完成所有标准初始化(包括__init_subclass__
)的现有 class 并修改 class。 由于这发生在__init_subclass__
之后, __init_subclass__
看不到@dataclass
执行的任何修改。
即使首先应用装饰器, D
仍然会通过A.__init_subclass__
中的检查,因为数据类装饰器无论如何都会将Dx
设置为x
字段的默认值,因此__init_subclass__
会找到x
的值。 在这种情况下,这恰好与您在原始 class 定义中设置的Dx
相同,但在您显式构造field
对象的情况下,它可以是不同的 object。
(另外,你可能想在not getattr(cls, 'x')
中写hasattr
而不是getattr
。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.