繁体   English   中英

带有 __init_subclass__ 的 dataclasses.dataclass

[英]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。 BCD都是子类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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM