Python数据类继承与类变量

问题描述

考虑以下示例代码

from dataclasses import dataclass,field
from typing import ClassVar


@dataclass
class Base:
    x: str = field(default='x',init=False)


@dataclass
class A(Base):
    name: str


@dataclass
class B(Base):
    name: str


a = A('test_a')
b = B('test_b')

a.x = 'y'
a.x  # prints 'y'
b.x  # prints 'x'

按预期打印“y”和“x”。

现在我想让 x 成为 dict 类型的 ClassVar:

from dataclasses import dataclass,field
from typing import ClassVar,Dict


@dataclass
class Base:
    x: ClassVar[Dict[str,str]] = field(default={'x': 'x'},init=False)


@dataclass
class A(Base):
    name: str


@dataclass
class B(Base):
    name: str


a = A('test_a')
b = B('test_b')

a.x['y'] = 'y'
a.x
b.x

然而,现在输出

a.x => {'x': 'x','y': 'y'}
b.x => {'x': 'x','y': 'y'}

我希望只有 a.x修改,而 b.x 保持认的初始值 `{'x': 'x'}。

如果该字段不是 ClassVar,那么我可以使用 default_factory=dict,但这不能与 ClassVar 结合使用,因为它返回错误

 Field cannot have a default factory

解决方法

也许使用 __post_init__ 你可以解决这个问题

@dataclass
class Base:
    # x: ClassVar[Dict[str,str]] = field(default=dict(val),init=False)
    def __post_init__(self) :
        self.x = {'x':'x'}
,

类变量在父类和所有子类之间共享,因此您似乎想要的(在父类中声明的类变量,但子类获得他们可以操作的自己的副本)在概念上是不可能的。

如果你想正确地做到这一点,你必须在每个孩子中重新声明类变量:

from dataclasses import dataclass
from typing import ClassVar,Dict


@dataclass
class Base:
    x: ClassVar[Dict[str,str]] = {'x': 'x'}


@dataclass
class A(Base):
    x: ClassVar[Dict[str,str]] = {'x': 'x'}
    name: str


@dataclass
class B(Base):
    x: ClassVar[Dict[str,str]] = {'x': 'x'}
    name: str

a = A('test_a')
b = B('test_b')

a.x['y'] = 'y'
a.x
b.x

现在给出

a.x => {'x': 'x','y': 'y'}
b.x => {'x': 'x'}

但如果这太麻烦或不切实际,我有这把漂亮的脚踏枪给你。它不使用 ClassVar,而是将您的需求作为函数显式编程到基类中,并使其看起来像带有 @property 装饰器的属性:

from dataclasses import dataclass
from typing import Dict


@dataclass
class Base:

    @property
    def x(self) -> Dict[str,str]:
        cls = type(self)
        # first call per child class instance will initialize a proxy
        if not hasattr(cls,"_x"):
            setattr(cls,"_x",{"x": "x"})  # store the actual state of "x" in "_x"
        return getattr(cls,"_x")


@dataclass
class A(Base):
    name: str


@dataclass
class B(Base):
    name: str


a_1 = A('test_a_1')
a_2 = A('test_a_2')
b = B('test_b')

a_1.x['y'] = 'y'
a_1.x
a_2.x
b.x

这也仅在子类实例之间正确共享 x,但您无需在每个新子类中多写一行:

a.x => {'x': 'x','y': 'y'}
a_1.x => {'x': 'x','y': 'y'}
b.x => {'x': 'x'}

一个警告是,与 ClassVar 不同,您不能在没有实例的情况下调用类上的属性,例如A.x 不起作用。但看来你无论如何都不想那样做。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...