🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 导航 - [索引](../genindex.xhtml "总目录") - [模块](../py-modindex.xhtml "Python 模块索引") | - [下一页](contextlib.xhtml "contextlib --- Utilities for with-statement contexts") | - [上一页](warnings.xhtml "warnings --- Warning control") | - ![](https://box.kancloud.cn/a721fc7ec672275e257bbbfde49a4d4e_16x16.png) - [Python](https://www.python.org/) » - zh\_CN 3.7.3 [文档](../index.xhtml) » - [Python 标准库](index.xhtml) » - [Python运行时服务](python.xhtml) » - $('.inline-search').show(0); | # [`dataclasses`](#module-dataclasses "dataclasses: Generate special methods on user-defined classes.") --- 数据类 **源码:** [Lib/dataclasses.py](https://github.com/python/cpython/tree/3.7/Lib/dataclasses.py) \[https://github.com/python/cpython/tree/3.7/Lib/dataclasses.py\] - - - - - - 这个模块提供了一个装饰器和一些函数,用于自动添加生成的 [special method](../glossary.xhtml#term-special-method)s ,例如 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 和 [`__repr__()`](../reference/datamodel.xhtml#object.__repr__ "object.__repr__") 到用户定义的类。 它最初描述于 [**PEP 557**](https://www.python.org/dev/peps/pep-0557) \[https://www.python.org/dev/peps/pep-0557\] 。 在这些生成的方法中使用的成员变量使用 [**PEP 526**](https://www.python.org/dev/peps/pep-0526) \[https://www.python.org/dev/peps/pep-0526\] 类型注释定义。例如这段代码: ``` @dataclass class InventoryItem: '''Class for keeping track of an item in inventory.''' name: str unit_price: float quantity_on_hand: int = 0 def total_cost(self) -> float: return self.unit_price * self.quantity_on_hand ``` 除其他事情外,将添加 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") ,其看起来像: ``` def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0): self.name = name self.unit_price = unit_price self.quantity_on_hand = quantity_on_hand ``` 请注意,此方法会自动添加到类中:它不会在上面显示的 `InventoryItem` 定义中直接指定。 3\.7 新版功能. ## 模块级装饰器、类和函数 `@``dataclasses.``dataclass`(*\**, *init=True*, *repr=True*, *eq=True*, *order=False*, *unsafe\_hash=False*, *frozen=False*)这个函数是 [decorator](../glossary.xhtml#term-decorator) ,用于将生成的 [special method](../glossary.xhtml#term-special-method) 添加到类中,如下所述。 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 装饰器检查类以找到 `field`。 `field` 被定义为具有 [类型标注](../glossary.xhtml#term-variable-annotation) 的类变量。除了下面描述的两个例外,在 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 中没有任何内容检查变量标注中指定的类型。 所有生成的方法中的字段顺序是它们在类定义中出现的顺序。 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 装饰器将向类中添加各种“dunder”方法,如下所述。如果类中已存在任何添加的方法,则行为取决于参数,如下所述。装饰器返回被调用的同一个类;没有创建新类。 如果 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 仅用作没有参数的简单装饰器,它就像它具有此签名中记录的默认值一样。也就是说,这三种 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 用法是等价的: ``` @dataclass class C: ... @dataclass() class C: ... @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False) class C: ... ``` [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 的参数有: - `init`: 如果为真值(默认),将生成一个 `__ init__()` 方法。 如果类已定义 `__ init__()` ,则忽略此参数。 - `repr` :如果为真值(默认),将生成一个 [`__repr__()`](../reference/datamodel.xhtml#object.__repr__ "object.__repr__") 方法。 生成的 repr 字符串将具有类名以及每个字段的名称和 repr ,按照它们在类中定义的顺序。不包括标记为从 repr 中排除的字段。 例如:`InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)`。 如果类已定义 [`__repr__()`](../reference/datamodel.xhtml#object.__repr__ "object.__repr__") ,则忽略此参数。 - `eq` :如果为true(默认值),将生成 [`__eq__()`](../reference/datamodel.xhtml#object.__eq__ "object.__eq__") 方法。此方法将类作为其字段的元组按顺序比较。比较中的两个实例必须是相同的类型。 如果类已定义 [`__eq__()`](../reference/datamodel.xhtml#object.__eq__ "object.__eq__") ,则忽略此参数。 - `order` :如果为真值(默认为 `False` ),则 [`__lt__()`](../reference/datamodel.xhtml#object.__lt__ "object.__lt__") 、 `__ le__()` 、 [`__gt__()`](../reference/datamodel.xhtml#object.__gt__ "object.__gt__") 和 [`__ge__()`](../reference/datamodel.xhtml#object.__ge__ "object.__ge__") 方法将生成。 这将类作为其字段的元组按顺序比较。比较中的两个实例必须是相同的类型。如果 `order` 为真值并且 `eq` 为假值 ,则引发 [`ValueError`](exceptions.xhtml#ValueError "ValueError") 。 如果类已经定义了 [`__lt__()`](../reference/datamodel.xhtml#object.__lt__ "object.__lt__") 、 [`__le__()`](../reference/datamodel.xhtml#object.__le__ "object.__le__") 、 [`__gt__()`](../reference/datamodel.xhtml#object.__gt__ "object.__gt__") 或者 [`__ge__()`](../reference/datamodel.xhtml#object.__ge__ "object.__ge__") 中的任意一个,将引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。 - `unsafe_hash` :如果为 `False` (默认值),则根据 `eq` 和 `frozen` 的设置方式生成 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法。 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 由内置的 [`hash()`](functions.xhtml#hash "hash") 使用,当对象被添加到散列集合(如字典和集合)时。有一个 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 意味着类的实例是不可变的。可变性是一个复杂的属性,取决于程序员的意图, [`__eq__()`](../reference/datamodel.xhtml#object.__eq__ "object.__eq__") 的存在性和行为,以及 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 装饰器中 `eq` 和 `frozen` 标志的值。 默认情况下, [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 不会隐式添加 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法,除非这样做是安全的。 它也不会添加或更改现有的明确定义的 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法。 设置类属性 `__hash__ = None` 对 Python 具有特定含义,如 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 文档中所述。 如果 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 没有显式定义,或者它被设置为 `None` ,那么 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") *可以* 添加一个隐式 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法。虽然不推荐,但你可以强制 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 用 `unsafe_hash=True` 创建一个 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法。 如果你的类在逻辑上是不可变的但实际仍然可变,则可能就是这种情况。这是一个特殊的用例,应该仔细考虑。 以下是隐式创建 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法的规则。请注意,你不能在数据类中都使用显式的 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法并设置 `unsafe_hash=True` ;这将导致 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。 如果 `eq` 和 `frozen` 都是 true,默认情况下 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 将为你生成一个 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法。如果 `eq` 为 true 且 `frozen` 为 false ,则 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 将被设置为 `None` ,标记它不可用(因为它是可变的)。如果 `eq` 为 false ,则 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 将保持不变,这意味着将使用超类的 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法(如果超类是 [`object`](functions.xhtml#object "object") ,这意味着它将回到基于id的hash)。 - `frozen` :如果为 true (默认值为 False ),则字段赋值将生成异常。这模拟了只读的冻结实例。如果 `__setattr __()` 或 [`__delattr__()`](../reference/datamodel.xhtml#object.__delattr__ "object.__delattr__") 在类中定义,则 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 被引发。请参阅下面的讨论。 `field`s 可以选择使用普通的 Python 语法指定默认值: ``` @dataclass class C: a: int # 'a' has no default value b: int = 0 # assign a default value for 'b' ``` 在这个例子中, `a` 和 `b` 都将包含在添加的 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 方法中,它们将被定义为: ``` def __init__(self, a: int, b: int = 0): ``` 如果没有默认值的字段跟在具有默认值的字段后,将引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。当这发生在单个类中时,或者作为类继承的结果时,都是如此。 `dataclasses.``field`(*\**, *default=MISSING*, *default\_factory=MISSING*, *repr=True*, *hash=None*, *init=True*, *compare=True*, *metadata=None*)对于常见和简单的用例,不需要其他功能。但是,有些数据类功能需要额外的每字段信息。为了满足这种对附加信息的需求,你可以通过调用提供的 [`field()`](#dataclasses.field "dataclasses.field") 函数来替换默认字段值。例如: ``` @dataclass class C: mylist: List[int] = field(default_factory=list) c = C() c.mylist += [1, 2, 3] ``` 如上所示, `MISSING` 值是一个 sentinel 对象,用于检测是否提供了 `default` 和 `default_factory` 参数。 使用此 sentinel 是因为 `None` 是 `default` 的有效值。没有代码应该直接使用 `MISSING` 值。 [`field()`](#dataclasses.field "dataclasses.field") 参数有: - `default` :如果提供,这将是该字段的默认值。这是必需的,因为 [`field()`](#dataclasses.field "dataclasses.field") 调用本身会替换一般的默认值。 - `default_factory` :如果提供,它必须是一个零参数可调用对象,当该字段需要一个默认值时,它将被调用。除了其他目的之外,这可以用于指定具有可变默认值的字段,如下所述。 同时指定 `default` 和 `default_factory` 将产生错误。 - `init` :如果为true(默认值),则该字段作为参数包含在生成的 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 方法中。 - `repr` :如果为true(默认值),则该字段包含在生成的 [`__repr__()`](../reference/datamodel.xhtml#object.__repr__ "object.__repr__") 方法返回的字符串中。 - `compare` :如果为true(默认值),则该字段包含在生成的相等性和比较方法中( [`__eq__()`](../reference/datamodel.xhtml#object.__eq__ "object.__eq__") , [`__gt__()`](../reference/datamodel.xhtml#object.__gt__ "object.__gt__") 等等)。 - `hash` :这可以是布尔值或 `None` 。如果为true,则此字段包含在生成的 [`__hash__()`](../reference/datamodel.xhtml#object.__hash__ "object.__hash__") 方法中。如果为 `None` (默认值),请使用 `compare` 的值,这通常是预期的行为。如果字段用于比较,则应在 hash 中考虑该字段。不鼓励将此值设置为 `None` 以外的任何值。 设置 `hash=False` 但 `compare=True` 的一个可能原因是,如果一个计算 hash 的代价很高的字段是检验等价性需要的,但还有其他字段可以计算类型的 hash 。 即使从 hash 中排除某个字段,它仍将用于比较。 - `metadata` :这可以是映射或 None 。 None 被视为一个空的字典。这个值包含在 `MappingProxyType()` 中,使其成为只读,并暴露在 [`Field`](#dataclasses.Field "dataclasses.Field") 对象上。数据类根本不使用它,它是作为第三方扩展机制提供的。多个第三方可以各自拥有自己的键值,以用作元数据中的命名空间。 如果通过调用 [`field()`](#dataclasses.field "dataclasses.field") 指定字段的默认值,则该字段的类属性将替换为指定的 `default` 值。如果没有提供 `default` ,那么将删除类属性。目的是在 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 装饰器运行之后,类属性将包含字段的默认值,就像指定了默认值一样。例如,之后: ``` @dataclass class C: x: int y: int = field(repr=False) z: int = field(repr=False, default=10) t: int = 20 ``` 类属性 `C.z` 将是 `10` ,类属性 `C.t` 将是 `20`,类属性 `C.x` 和 `C.y` 将不设置。 *class* `dataclasses.``Field`[`Field`](#dataclasses.Field "dataclasses.Field") 对象描述每个定义的字段。这些对象在内部创建,并由 [`fields()`](#dataclasses.fields "dataclasses.fields") 模块级方法返回(见下文)。用户永远不应该直接实例化 [`Field`](#dataclasses.Field "dataclasses.Field") 对象。 其有文档的属性是: > - `name` :字段的名字。 > - `type` :字段的类型。 > - `default` 、 `default_factory` 、 `init` 、 `repr` 、 `hash` 、 `compare` 以及 `metadata` 与具有和 [`field()`](#dataclasses.field "dataclasses.field") 声明中相同的意义和值。 可能存在其他属性,但它们是私有的,不能被审查或依赖。 `dataclasses.``fields`(*class\_or\_instance*)返回 [`Field`](#dataclasses.Field "dataclasses.Field") 对象的元组,用于定义此数据类的字段。 接受数据类或数据类的实例。如果没有传递一个数据类或实例将引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。 不返回 `ClassVar` 或 `InitVar` 的伪字段。 `dataclasses.``asdict`(*instance*, *\**, *dict\_factory=dict*)将数据类 `instance` 转换为字典(使用工厂函数 `dict_factory` )。每个数据类都转换为其字段的字典,如 `name: value` 对。数据类、字典、列表和元组被递归。例如: ``` @dataclass class Point: x: int y: int @dataclass class C: mylist: List[Point] p = Point(10, 20) assert asdict(p) == {'x': 10, 'y': 20} c = C([Point(0, 0), Point(10, 4)]) assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]} ``` 引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 如果 `instance` 不是数据类实例。 `dataclasses.``astuple`(*instance*, *\**, *tuple\_factory=tuple*)将数据类 `instance` 转换为元组(通过使用工厂函数 `tuple_factory` )。每个数据类都转换为其字段值的元组。数据类、字典、列表和元组被递归。 继续前一个例子: ``` assert astuple(p) == (10, 20) assert astuple(c) == ([(0, 0), (10, 4)],) ``` 引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 如果 `instance` 不是数据类实例。 `dataclasses.``make_dataclass`(*cls\_name*, *fields*, *\**, *bases=()*, *namespace=None*, *init=True*, *repr=True*, *eq=True*, *order=False*, *unsafe\_hash=False*, *frozen=False*)创建一个名为 `cls_name` 的新数据类,字段为 `fields` 中定义的字段,基类为 `bases` 中给出的基类,并使用 `namespace` 中给出的命名空间进行初始化。 `fields` 是一个可迭代的元素,每个元素都是 `name` 、 `(name, type)` 或 `(name, type, Field)` 。 如果只提供``name`` , `type` 为 `typing.Any` 。 `init` 、 `repr` 、 `eq` 、 `order` 、 `unsafe_hash` 和 `frozen` 的值与它们在 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 中的含义相同。 此函数不是严格要求的,因为用于任何创建带有 `__annotations__` 的新类的 Python 机制都可以应用 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 函数将该类转换为数据类。提供此功能是为了方便。例如: ``` C = make_dataclass('C', [('x', int), 'y', ('z', int, field(default=5))], namespace={'add_one': lambda self: self.x + 1}) ``` 等价于 ``` @dataclass class C: x: int y: 'typing.Any' z: int = 5 def add_one(self): return self.x + 1 ``` `dataclasses.``replace`(*instance*, *\*\*changes*)创建一个 `instance` 相同类型的新对象,用 `changes` 中的值替换字段。如果 `instance` 不是数据类,则引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。如果 `changes` 中的值没有指定字段,则引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。 新返回的对象通过调用数据类的 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 方法创建。这确保了如果存在 `__post_init__()` ,其也被调用。 如果存在没有默认值的仅初始化变量,必须在调用 [`replace()`](#dataclasses.replace "dataclasses.replace") 时指定,以便它们可以传递给 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 和 `__post_init__()` 。 `changes` 包含任何定义为 `init=False` 的字段是错误的。在这种情况下会引发 [`ValueError`](exceptions.xhtml#ValueError "ValueError") 。 提前提醒 `init=False` 字段在调用 [`replace()`](#dataclasses.replace "dataclasses.replace") 时的工作方式。如果它们完全被初始化的话,它们不是从源对象复制的,而是在 `__post_init__()` 中初始化。估计 `init=False` 字段很少能被正确地使用。如果使用它们,那么使用备用类构造函数或者可能是处理实例复制的自定义 `replace()` (或类似命名的)方法可能是明智的。 `dataclasses.``is_dataclass`(*class\_or\_instance*)如果其参数是数据类或数据类实例,则返回 True ,否则返回 False 。 如果你需要知道一个类是否是一个数据类的实例(而不是一个数据类本身),那么再添加一个 `not isinstance(obj, type)` 检查: ``` def is_dataclass_instance(obj): return is_dataclass(obj) and not isinstance(obj, type) ``` ## 初始化后处理 生成的 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 代码将调用一个名为 `__post_init__()` 的方法,如果在类上已经定义了 `__post_init__()` 。它通常被称为 `self.__post_init__()` 。但是,如果定义了任何 `InitVar` 字段,它们也将按照它们在类中定义的顺序传递给 `__post_init__()` 。 如果没有 `__ init__()` 方法生成,那么 `__post_init__()` 将不会被自动调用。 在其他用途中,这允许初始化依赖于一个或多个其他字段的字段值。例如: ``` @dataclass class C: a: float b: float c: float = field(init=False) def __post_init__(self): self.c = self.a + self.b ``` 有关将参数传递给 `__post_init__()` 的方法,请参阅下面有关仅初始化变量的段落。另请参阅关于 [`replace()`](#dataclasses.replace "dataclasses.replace") 处理 `init=False` 字段的警告。 ## 类变量 两个地方 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 实际检查字段类型的之一是确定字段是否是如 [**PEP 526**](https://www.python.org/dev/peps/pep-0526) \[https://www.python.org/dev/peps/pep-0526\] 所定义的类变量。它通过检查字段的类型是否为 `typing.ClassVar` 来完成此操作。如果一个字段是一个 `ClassVar` ,它将被排除在考虑范围之外,并被数据类机制忽略。这样的 `ClassVar` 伪字段不会由模块级的 [`fields()`](#dataclasses.fields "dataclasses.fields") 函数返回。 ## 仅初始化变量 另一个 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 检查类型注解地方是为了确定一个字段是否是一个仅初始化变量。它通过查看字段的类型是否为 `dataclasses.InitVar` 类型来实现。如果一个字段是一个 `InitVar` ,它被认为是一个称为仅初始化字段的伪字段。因为它不是一个真正的字段,所以它不会被模块级的 [`fields()`](#dataclasses.fields "dataclasses.fields") 函数返回。仅初始化字段作为参数添加到生成的 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 方法中,并传递给可选的 `__post_init__()` 方法。数据类不会使用它们。 例如,假设一个字段将从数据库初始化,如果在创建类时未提供其值: ``` @dataclass class C: i: int j: int = None database: InitVar[DatabaseType] = None def __post_init__(self, database): if self.j is None and database is not None: self.j = database.lookup('j') c = C(10, database=my_database) ``` 在这种情况下, [`fields()`](#dataclasses.fields "dataclasses.fields") 将返回 `i` 和 `j` 的 [`Field`](#dataclasses.Field "dataclasses.Field") 对象,但不包括 `database` 。 ## 冻结的实例 无法创建真正不可变的 Python 对象。但是,通过将 `frozen=True` 传递给 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 装饰器,你可以模拟不变性。在这种情况下,数据类将向类添加 [`__setattr__()`](../reference/datamodel.xhtml#object.__setattr__ "object.__setattr__") 和 [`__delattr__()`](../reference/datamodel.xhtml#object.__delattr__ "object.__delattr__") 方法。 些方法在调用时会引发 [`FrozenInstanceError`](#dataclasses.FrozenInstanceError "dataclasses.FrozenInstanceError") 。 使用 `frozen=True` 时会有很小的性能损失: `__ init__()` 不能使用简单的赋值来初始化字段,并必须使用 `object.__ setattr__()` 。 ## 继承 当数组由 [`dataclass()`](#dataclasses.dataclass "dataclasses.dataclass") 装饰器创建时,它会查看反向 MRO 中的所有类的基类(即从 [`object`](functions.xhtml#object "object") 开始 ),并且对于它找到的每个数据类, 将该基类中的字段添加到字段的有序映射中。添加完所有基类字段后,它会将自己的字段添加到有序映射中。所有生成的方法都将使用这种组合的,计算的有序字段映射。由于字段是按插入顺序排列的,因此派生类会重载基类。一个例子: ``` @dataclass class Base: x: Any = 15.0 y: int = 0 @dataclass class C(Base): z: int = 10 x: int = 15 ``` 最后的字段列表依次是 `x` 、 `y` 、 `z` 。 `x` 的最终类型是 `int` ,如类 `C` 中所指定的那样。 为 `C` 生成的 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 方法看起来像: ``` def __init__(self, x: int = 15, y: int = 0, z: int = 10): ``` ## 默认工厂函数 > 如果一个 [`field()`](#dataclasses.field "dataclasses.field") 指定了一个 `default_factory` ,当需要该字段的默认值时,将使用零参数调用它。例如,要创建列表的新实例,请使用: > > > ``` > mylist: list = field(default_factory=list) > > ``` > > > > > 如果一个字段被排除在 [`__init__()`](../reference/datamodel.xhtml#object.__init__ "object.__init__") 之外(使用 `init=False` )并且字段也指定 `default_factory` ,则默认的工厂函数将始终从生成的 `__ init__()` 函数调用。发生这种情况是因为没有其他方法可以为字段提供初始值。 ## 可变的默认值 > Python 在类属性中存储默认成员变量值。思考这个例子,不使用数据类: > > > ``` > class C: > x = [] > def add(self, element): > self.x.append(element) > > o1 = C() > o2 = C() > o1.add(1) > o2.add(2) > assert o1.x == [1, 2] > assert o1.x is o2.x > > ``` > > > > > 请注意,类 `C` 的两个实例共享相同的类变量 `x` ,如预期的那样。 > > 使用数据类, *如果* 此代码有效: > > > ``` > @dataclass > class D: > x: List = [] > def add(self, element): > self.x += element > > ``` > > > > > 它生成的代码类似于: > > > ``` > class D: > x = [] > def __init__(self, x=x): > self.x = x > def add(self, element): > self.x += element > > assert D().x is D().x > > ``` > > > > > 这与使用类 `C` 的原始示例具有相同的问题。也就是说,在创建类实例时没有为 `x` 指定值的类 `D` 的两个实例将共享相同的 `x` 副本。由于数据类只使用普通的 Python 类创建,因此它们也会共享此行为。数据类没有通用的方法来检测这种情况。相反,如果数据类检测到类型为 `list` 、 `dict` 或 `set` 的默认参数,则会引发 [`TypeError`](exceptions.xhtml#TypeError "TypeError") 。这是一个部分解决方案,但它可以防止许多常见错误。 > > 使用默认工厂函数是一种创建可变类型新实例的方法,并将其作为字段的默认值: > > > ``` > @dataclass > class D: > x: list = field(default_factory=list) > > assert D().x is not D().x > > ``` ## 异常 *exception* `dataclasses.``FrozenInstanceError`在使用 `frozen=True` 定义的数据类上调用隐式定义的 [`__setattr__()`](../reference/datamodel.xhtml#object.__setattr__ "object.__setattr__") 或 [`__delattr__()`](../reference/datamodel.xhtml#object.__delattr__ "object.__delattr__") 时引发。 ### 导航 - [索引](../genindex.xhtml "总目录") - [模块](../py-modindex.xhtml "Python 模块索引") | - [下一页](contextlib.xhtml "contextlib --- Utilities for with-statement contexts") | - [上一页](warnings.xhtml "warnings --- Warning control") | - ![](https://box.kancloud.cn/a721fc7ec672275e257bbbfde49a4d4e_16x16.png) - [Python](https://www.python.org/) » - zh\_CN 3.7.3 [文档](../index.xhtml) » - [Python 标准库](index.xhtml) » - [Python运行时服务](python.xhtml) » - $('.inline-search').show(0); | © [版权所有](../copyright.xhtml) 2001-2019, Python Software Foundation. Python 软件基金会是一个非盈利组织。 [请捐助。](https://www.python.org/psf/donations/) 最后更新于 5月 21, 2019. [发现了问题](../bugs.xhtml)? 使用[Sphinx](http://sphinx.pocoo.org/)1.8.4 创建。