具有 Iterable 字段的冻结和可散列数据类
frozen and hashable dataclass with Iterable field
现在我终于放弃了对 Python 2 的支持,我正在从 attrs
迁移到 Python 3 dataclasses
,有一个问题我特别苦恼和。
假设我有一个冻结且可哈希的 class MyClass
,其中一个字段 my_field
类型为 tuple
。
感谢 attrs
转换器,我能够提供灵活的 API,客户端能够实例化 my_field
等各种类型,例如 list
,set
或 dict_keys
。在 class 创建之前,它们都会自动转换为 tuple
。
我可以用 dataclasses
保留这个 API 吗?
应要求,一个小代码示例:
@attr.s(frozen=True, hash=True)
class MyClass:
my_field = attr.ib(default=tuple(), converter=tuple)
print(MyClass([1, 2, 3]))
不,转换器是数据类 PEP 选择不实现以保持简单的东西之一。 http://www.attrs.org/en/stable/why.html#data-classes再提几个。
DC 严格来说是 attrs
的一个子集,它不太可能永远改变。
在 post_init 步骤中通过基础 class 设置属性值似乎可行:
@dataclass(frozen=True)
class MyClass:
my_field: Sequence[str]
def __post_init__(self):
super().__setattr__('my_field', tuple(getattr(self, 'my_field')))
Mixin 实现:
class ValidationError(AttributeError):
pass
class ConversionValidationField(Field):
def __init__(self, default=MISSING, default_factory=MISSING, init=True, repr=True,
hash=None, compare=True, metadata=None, converter=None, validator=None):
self.converter = converter
self.validator = validator
super().__init__(default, default_factory, init, repr, hash, compare, metadata)
class ConversionValidationMixin:
def __post_init__(self):
for field in fields(self):
if isinstance(field, ConversionValidationField):
if field.converter:
super().__setattr__(field.name, field.converter(getattr(self, field.name)))
if field.validator:
if not field.validator(getattr(self, field.name)):
raise ValidationError('Validation failed for {}.'.format(field.name))
现在我终于放弃了对 Python 2 的支持,我正在从 attrs
迁移到 Python 3 dataclasses
,有一个问题我特别苦恼和。
假设我有一个冻结且可哈希的 class MyClass
,其中一个字段 my_field
类型为 tuple
。
感谢 attrs
转换器,我能够提供灵活的 API,客户端能够实例化 my_field
等各种类型,例如 list
,set
或 dict_keys
。在 class 创建之前,它们都会自动转换为 tuple
。
我可以用 dataclasses
保留这个 API 吗?
应要求,一个小代码示例:
@attr.s(frozen=True, hash=True)
class MyClass:
my_field = attr.ib(default=tuple(), converter=tuple)
print(MyClass([1, 2, 3]))
不,转换器是数据类 PEP 选择不实现以保持简单的东西之一。 http://www.attrs.org/en/stable/why.html#data-classes再提几个。
DC 严格来说是 attrs
的一个子集,它不太可能永远改变。
在 post_init 步骤中通过基础 class 设置属性值似乎可行:
@dataclass(frozen=True)
class MyClass:
my_field: Sequence[str]
def __post_init__(self):
super().__setattr__('my_field', tuple(getattr(self, 'my_field')))
Mixin 实现:
class ValidationError(AttributeError):
pass
class ConversionValidationField(Field):
def __init__(self, default=MISSING, default_factory=MISSING, init=True, repr=True,
hash=None, compare=True, metadata=None, converter=None, validator=None):
self.converter = converter
self.validator = validator
super().__init__(default, default_factory, init, repr, hash, compare, metadata)
class ConversionValidationMixin:
def __post_init__(self):
for field in fields(self):
if isinstance(field, ConversionValidationField):
if field.converter:
super().__setattr__(field.name, field.converter(getattr(self, field.name)))
if field.validator:
if not field.validator(getattr(self, field.name)):
raise ValidationError('Validation failed for {}.'.format(field.name))