Python 数据class 继承 class 变量
Python dataclass inheritance with class variables
考虑以下示例代码
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
结合使用,因为它 returns 错误
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'}
Class 变量在 parent 和所有 child class 之间共享,所以你似乎想要什么(一个 class 变量在 parent 中声明,但 child classes 得到他们自己的副本,他们可以操纵)在概念上是不可能的。
如果你想正确地做到这一点,你必须 re-declare 每个 child:
中的 class 变量
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
s,它只是将您的需求作为函数显式编程到基础 class 中,并使其看起来像带有 @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
这也仅在 child-class 个实例之间正确共享 x
,但您无需在每个新的 child:
中写入额外的一行
a.x => {'x': 'x', 'y': 'y'}
a_1.x => {'x': 'x', 'y': 'y'}
b.x => {'x': 'x'}
需要注意的是,与 ClassVar
s 不同,您不能在没有实例的情况下在 class 上调用 属性,例如A.x
不会工作。但你似乎并没有尝试这样做。
考虑以下示例代码
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
:
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
结合使用,因为它 returns 错误
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'}
Class 变量在 parent 和所有 child class 之间共享,所以你似乎想要什么(一个 class 变量在 parent 中声明,但 child classes 得到他们自己的副本,他们可以操纵)在概念上是不可能的。
如果你想正确地做到这一点,你必须 re-declare 每个 child:
中的 class 变量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
s,它只是将您的需求作为函数显式编程到基础 class 中,并使其看起来像带有 @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
这也仅在 child-class 个实例之间正确共享 x
,但您无需在每个新的 child:
a.x => {'x': 'x', 'y': 'y'}
a_1.x => {'x': 'x', 'y': 'y'}
b.x => {'x': 'x'}
需要注意的是,与 ClassVar
s 不同,您不能在没有实例的情况下在 class 上调用 属性,例如A.x
不会工作。但你似乎并没有尝试这样做。