数据类默认使用 InitVar 来设置实例属性

问题描述 投票:0回答:1

当您为数据类中的

InitVar
类型变量提供默认值时,属性 对于该变量已设置(实际上,对于缺少默认值的变量,该属性似乎是 主动删除
dataclasses:_process_class()
)。

from dataclasses import dataclass, InitVar, fields

@dataclass
class HasDefault:
    abc: int = 18
    xyz: InitVar[str] = 'oops'

    def __post_init__(self, xyz):
        self.abc += len(xyz)

@dataclass
class NoHasDefault:
    abc: int
    xyz: InitVar[str]

    def __post_init__(self, xyz):
        self.abc += len(xyz)


hd = HasDefault(abc=42, xyz='so long...')
print('field names', [field.name for field in fields(hd)])
print(hd, hd.abc, hd.xyz)
print()
hd2 = HasDefault(abc=42)
print('field names', [field.name for field in fields(hd2)])
print(hd2, hd2.abc, hd2.xyz)
print()
hd3 = HasDefault()
print('field names', [field.name for field in fields(hd3)])
print(hd3, hd3.abc, hd3.xyz)
print()
nhd = NoHasDefault(abc=42, xyz='so long...')
print('field names', [field.name for field in fields(nhd)])
print(nhd, getattr(nhd, 'xyz', 'no attribute xyz'))

给出:

field names ['abc']
HasDefault(abc=52) 52 oops

field names ['abc']
HasDefault(abc=46) 46 oops

field names ['abc']
HasDefault(abc=22) 22 oops

field names ['abc']
NoHasDefault(abc=52) no attribute xyz

为什么属性

xyz
InitVar 类型变量设置
at all
(当有默认值时)?我希望永远不会设置此属性,因为它仅用于初始化。这是一个错误吗?

mypy
实际上抱怨这些实例没有属性
xyz
,所以它似乎以不同的方式处理
InitVar

因此,我需要添加一些额外的行来加载这样的

dataclass
从 YAML 以相同的方式工作,通常是来自 YAML 映射的值 将用于设置属性,而不是默认值。所以你可以做的(在 `ruamel.yaml>0.17.34 中)是:

from dataclasses import dataclass, InitVar
from typing import ClassVar
from ruamel.yaml import YAML

yaml = YAML()

@yaml.register_class
@dataclass
class HasDefault:
    yaml_tag: ClassVar = '!has_default'  # if not set the class name is taken ('!HasDefault')
    abc: int
    xyz: InitVar[str] = 'oops'

    def __post_init__(self, xyz):
        self.abc += len(xyz)

yaml_str = """\
!has_default
abc: 42
xyz: hello world
"""

data = yaml.load(yaml_str)
print(data, data.xyz == 'oops')

给予:

HasDefault(abc=53) True

否则

data.xyz
将等于
hello world

python default-value python-dataclasses
1个回答
0
投票

为什么要为 InitVar 类型变量设置属性 xyz(当有默认值时)?我希望永远不会设置此属性,因为它仅用于初始化。这是bug吗?

医生说:

如果一个字段是

InitVar
,则它被视为伪字段,称为伪字段 仅初始化字段。由于它不是一个真实的字段,因此它不会被返回 模块级
fields()
函数。仅初始化字段添加为 生成的
__init__()
方法的参数,并传递给 可选的
__post_init__
方法。它们不被以其他方式使用 数据类。

因此 dataclasses 仅从

InitVars
上下文中排除
fields
。 但是提到的短语 仅初始化字段作为参数添加到生成的
__init__()
方法
意味着这些仍将在初始化阶段作为类属性添加。


另一个细微差别是我们不允许混合默认/非默认

initVars
,例如:

@dataclass
class HasDefault:
    abc: int = 18
    xyz: InitVar[str]
    iv: InitVar[int] = ''

    def __post_init__(self, xyz, iv):
        self.abc += len(xyz) 

因为它会抛出错误:

TypeError: non-default argument 'xyz' follows default argument

© www.soinside.com 2019 - 2024. All rights reserved.