当 class 变量被分配为列表时,python 中的数据class 不会引发错误(但会出现键入提示)
Dataclass in python does not raise error when the class variable is assigned as a list (but does with typing hints)
我只是想让自己熟悉 python 中的数据class。我从网上的一些阅读中学到的一件事是,我们可以将带有可变 class 变量(这是一件坏事)的常规 class 定义转换为 dataclass ,这样可以防止它。例如:
常规 class:
class A:
a = []
def __init__(self):
self.b = 1
这可能会导致不同实例共享相同 class 变量 a
并在不知不觉中修改 a
的潜在问题。
和数据class:
@dataclass
class A:
a: list = []
def __init__(self):
self.b = 1
这不允许我通过引发错误来写这个 class:
ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory
但是,如果我简单地去掉类型注释:
@dataclass
class A:
a = []
def __init__(self):
self.b = 1
完全没有投诉,a
仍然在不同实例之间共享。
这是预期的吗?
为什么简单类型注释会改变 class 变量的行为?
(我正在使用 python 3.7.6
)
当你申报时
@dataclass
class A:
a = []
def __init__(self):
self.b = 1
a
不是数据class 字段。参考:https://github.com/ericvsmith/dataclasses/issues/2#issuecomment-302987864
您可以在声明 class 后查看 __dataclass_fields__
和 __annotations__
字段。
In [55]: @dataclass
...: class A:
...: a: list = field(default_factory=list)
...:
...: def __init__(self):
...: self.b = 1
...:
In [56]: A.__dict__
Out[56]:
mappingproxy({'__module__': '__main__',
'__annotations__': {'a': list},
'__init__': <function __main__.A.__init__(self)>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': 'A()',
'__dataclass_params__': _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False),
'__dataclass_fields__': {'a': Field(name='a',type=<class 'list'>,default=<dataclasses._MISSING_TYPE object at 0x7f8a27ada250>,default_factory=<class 'list'>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)},
'__repr__': <function dataclasses.__repr__(self)>,
'__eq__': <function dataclasses.__eq__(self, other)>,
'__hash__': None})
In [57]: @dataclass
...: class A:
...: a = []
...:
...: def __init__(self):
...: self.b = 1
...:
In [58]: A.__dict__
Out[58]:
mappingproxy({'__module__': '__main__',
'a': [],
'__init__': <function __main__.A.__init__(self)>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': 'A()',
'__dataclass_params__': _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False),
'__dataclass_fields__': {},
'__repr__': <function dataclasses.__repr__(self)>,
'__eq__': <function dataclasses.__eq__(self, other)>,
'__hash__': None})
来自 PEP 557:
dataclass 装饰器检查 class 以查找字段。字段定义为 __annotations__
中标识的任何变量。即,具有类型注释的变量。参考:
检查仅发生在数据class 字段上而不发生在 class 变量上,这是导致错误的字段检查
if f._field_type is _FIELD and isinstance(f.default, (list, dict, set)):
为什么不允许可变类型:https://docs.python.org/3/library/dataclasses.html#mutable-default-values
我只是想让自己熟悉 python 中的数据class。我从网上的一些阅读中学到的一件事是,我们可以将带有可变 class 变量(这是一件坏事)的常规 class 定义转换为 dataclass ,这样可以防止它。例如:
常规 class:
class A:
a = []
def __init__(self):
self.b = 1
这可能会导致不同实例共享相同 class 变量 a
并在不知不觉中修改 a
的潜在问题。
和数据class:
@dataclass
class A:
a: list = []
def __init__(self):
self.b = 1
这不允许我通过引发错误来写这个 class:
ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory
但是,如果我简单地去掉类型注释:
@dataclass
class A:
a = []
def __init__(self):
self.b = 1
完全没有投诉,a
仍然在不同实例之间共享。
这是预期的吗?
为什么简单类型注释会改变 class 变量的行为?
(我正在使用 python 3.7.6
)
当你申报时
@dataclass
class A:
a = []
def __init__(self):
self.b = 1
a
不是数据class 字段。参考:https://github.com/ericvsmith/dataclasses/issues/2#issuecomment-302987864
您可以在声明 class 后查看 __dataclass_fields__
和 __annotations__
字段。
In [55]: @dataclass
...: class A:
...: a: list = field(default_factory=list)
...:
...: def __init__(self):
...: self.b = 1
...:
In [56]: A.__dict__
Out[56]:
mappingproxy({'__module__': '__main__',
'__annotations__': {'a': list},
'__init__': <function __main__.A.__init__(self)>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': 'A()',
'__dataclass_params__': _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False),
'__dataclass_fields__': {'a': Field(name='a',type=<class 'list'>,default=<dataclasses._MISSING_TYPE object at 0x7f8a27ada250>,default_factory=<class 'list'>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)},
'__repr__': <function dataclasses.__repr__(self)>,
'__eq__': <function dataclasses.__eq__(self, other)>,
'__hash__': None})
In [57]: @dataclass
...: class A:
...: a = []
...:
...: def __init__(self):
...: self.b = 1
...:
In [58]: A.__dict__
Out[58]:
mappingproxy({'__module__': '__main__',
'a': [],
'__init__': <function __main__.A.__init__(self)>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': 'A()',
'__dataclass_params__': _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False),
'__dataclass_fields__': {},
'__repr__': <function dataclasses.__repr__(self)>,
'__eq__': <function dataclasses.__eq__(self, other)>,
'__hash__': None})
来自 PEP 557:
dataclass 装饰器检查 class 以查找字段。字段定义为 __annotations__
中标识的任何变量。即,具有类型注释的变量。参考:
检查仅发生在数据class 字段上而不发生在 class 变量上,这是导致错误的字段检查
if f._field_type is _FIELD and isinstance(f.default, (list, dict, set)):
为什么不允许可变类型:https://docs.python.org/3/library/dataclasses.html#mutable-default-values