dataclasses
---数据类¶
源代码: Lib/dataclasses.py
此模块提供一个装饰器和自动添加生成的 special method 例如 __init__()
和 __repr__()
到用户定义的类。最初的描述是 PEP 557 .
在这些生成的方法中使用的成员变量是使用 PEP 526 键入批注。例如,此代码:
from dataclasses import dataclass
@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__()
看起来像是:
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 用于添加生成的 special method s到类,如下所述。
这个
dataclass()
decorator检查类以查找field
美国field
定义为具有 type annotation . 除了下面描述的两个例外,没有dataclass()
检查变量批注中指定的类型。所有生成的方法中字段的顺序是它们在类定义中出现的顺序。
这个
dataclass()
decorator将向类添加各种“dunder”方法,如下所述。如果类中已经存在任何添加的方法,则行为取决于参数,如下所述。decorator返回调用的同一个类;不创建新类。如果
dataclass()
就像一个没有参数的简单装饰器一样使用,它的作用就像在这个签名中记录了默认值一样。也就是说,这三种用途dataclass()
相当于:@dataclass class C: ... @dataclass() class C: ... @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False) class C: ...
参数
dataclass()
是:init
:如果为真(默认值),则为__init__()
将生成方法。如果类已经定义
__init__()
,此参数被忽略。repr
:如果为真(默认值),则为__repr__()
将生成方法。生成的repr字符串将具有类名和每个字段的名称和repr,其顺序在类中定义。标记为从报告中排除的字段不包括在内。例如:InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)
.如果类已经定义
__repr__()
,此参数被忽略。eq
:如果为真(默认值),则__eq__()
将生成方法。此方法按顺序将类作为其字段的元组进行比较。比较中的两个实例的类型必须相同。如果类已经定义
__eq__()
,此参数被忽略。order
:如果为真(默认为False
)__lt__()
,__le__()
,__gt__()
和__ge__()
将生成方法。它们按照顺序将类与字段的元组进行比较。比较中的两个实例的类型必须相同。如果order
是真的eq
是假的,AValueError
提高了。如果类已经定义了
__lt__()
,__le__()
,__gt__()
或__ge__()
然后TypeError
提高了。unsafe_hash
如果False
(默认),a__hash__()
方法是根据eq
和frozen
被设置。__hash__()
是内置的hash()
以及将对象添加到hash集合(如字典和集合)时。有一个__hash__()
表示类的实例是不可变的。易变性是一个复杂的属性,它取决于程序员的意图、存在和行为__eq__()
以及eq
和frozen
中的flagdataclass()
decorator。默认情况下,
dataclass()
不会隐式添加__hash__()
方法,除非这样做是安全的。它也不会添加或更改显式定义的现有__hash__()
方法。设置类属性__hash__ = None
对python有特定的意义,如中所述__hash__()
文档。如果
__hash__()
未显式定义,或者如果将其设置为None
然后dataclass()
may 添加隐式__hash__()
方法。虽然不推荐,但您可以强制dataclass()
创建一个__hash__()
方法与unsafe_hash=True
. 如果您的类在逻辑上是不可变的,但仍然可以发生变化,那么就可能发生这种情况。这是一个专门的用例,应该仔细考虑。以下是管理隐式创建
__hash__()
方法。请注意,不能同时使用__hash__()
数据类和集合中的方法unsafe_hash=True
;这将导致TypeError
.如果
eq
和frozen
默认情况下都是真的dataclass()
将生成一个__hash__()
方法。如果eq
是真的frozen
__hash__()
将被设置为None
,将其标记为不可清洗(因为它是可变的)。如果eq
__hash__()
将保持原样,意味着__hash__()
将使用超类的方法(如果超类是object
,这意味着它将返回到基于ID的散列)。frozen
:如果为真(默认为False
),分配给字段将生成异常。这将模拟只读冻结实例。如果__setattr__()
或__delattr__()
在类中定义,然后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__()
方法,其定义为:def __init__(self, a: int, b: int = 0):
TypeError
如果没有默认值的字段跟在具有默认值的字段后面,则将引发。无论是在单个类中发生这种情况,还是由于类继承,这都是正确的。
- dataclasses.field(*, default=MISSING, default_factory=MISSING, repr=True, hash=None, init=True, compare=True, metadata=None)¶
对于常见和简单的用例,不需要其他功能。但是,有些数据类功能需要每个字段附加信息。为了满足对附加信息的需求,可以使用对提供的
field()
功能。例如::@dataclass class C: mylist: list[int] = field(default_factory=list) c = C() c.mylist += [1, 2, 3]
如上图所示,
MISSING
值是用于检测default
和default_factory
提供参数。使用这个哨兵是因为None
是的有效值default
.任何代码都不应直接使用MISSING
价值。参数
field()
是:default
:如果提供,这将是此字段的默认值。这是需要的,因为field()
调用本身替换默认值的正常位置。default_factory
:如果提供,则必须是可调用的零参数,当此字段需要默认值时将调用该参数。除其他用途外,它还可以用于指定具有可变默认值的字段,如下所述。指定两者都是错误的default
和default_factory
.init
:如果为真(默认值),则此字段将作为生成的__init__()
方法。repr
:如果为真(默认值),则此字段包含在由生成的__repr__()
方法。compare
:如果为真(默认值),则此字段包含在生成的相等和比较方法中。 (__eq__()
,__gt__()
等。hash
:这可能是一个bool或None
. 如果为真,则此字段包含在生成的__hash__()
方法。如果None
(默认值),使用compare
:这通常是预期的行为。如果用于比较,则应在hash中考虑字段。将此值设置为除None
不鼓励。设置的一个可能原因
hash=False
但是compare=True
如果一个字段的hash值计算起来很昂贵,那么该字段就需要进行相等性测试,并且还有其他字段对该类型的hash值起作用。即使字段从hash中排除,它仍将用于比较。metadata
:这可以是映射,也可以是无映射。无被视为空dict。此值被封装在MappingProxyType()
使其只读,并在Field
对象。数据类根本不使用它,它作为第三方扩展机制提供。多个第三方可以各自拥有自己的密钥,用作元数据中的命名空间。
如果字段的默认值是通过调用
field()
,则该字段的class属性将替换为指定的default
价值。如果没有default
提供,则类属性将被删除。其目的是在dataclass()
decorator运行时,类属性将全部包含字段的默认值,就像指定了默认值本身一样。例如,之后::@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
对象描述每个定义的字段。这些对象是在内部创建的,并由fields()
模块级方法(见下文)。用户不应实例化Field
直接对象。其文件化属性包括:name
:字段的名称。type
:字段的类型。default
,default_factory
,init
,repr
,hash
,compare
和metadata
具有相同的含义和价值field()
宣言。
其他属性可能存在,但它们是私有的,不能检查或依赖。
- dataclasses.fields(class_or_instance)¶
返回的元组
Field
定义此数据类的字段的对象。接受数据类或数据类的实例。引发TypeError
如果没有传递一个数据类或实例。不返回以下伪字段:ClassVar
或InitVar
.
- dataclasses.asdict(instance, *, dict_factory=dict)¶
转换数据类
instance
到dict(通过使用factory函数dict_factory
)每个数据类都转换为其字段的dict,如name: value
对。数据类、dict、list和tuples递归到中。例如::@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
如果instance
不是DataClass实例。
- dataclasses.astuple(instance, *, tuple_factory=tuple)¶
转换数据类
instance
到元组(通过使用factory函数tuple_factory
)每个数据类都转换为其字段值的元组。数据类、dict、list和tuples递归到中。从上一个示例继续:
assert astuple(p) == (10, 20) assert astuple(c) == ([(0, 0), (10, 4)],)
引发
TypeError
如果instance
不是DataClass实例。
- 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
是一个iterable,其元素分别为name
,(name, type)
或(name, type, Field)
. 如果只是name
提供,typing.Any
用于type
. 价值观init
,repr
,eq
,order
,unsafe_hash
和frozen
与它们在中的含义相同dataclass()
.此函数不是严格要求的,因为任何用于创建新类的python机制
__annotations__
然后可以应用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
. 如果值在changes
不指定字段,引发TypeError
.新返回的对象是通过调用
__init__()
数据类的方法。这确保了__post_init__()
如果存在,也被称为。只有不带默认值的init变量(如果存在)必须在调用时指定
replace()
以便将它们传递给__init__()
和__post_init__()
.这是一个错误
changes
包含定义为具有init=False
. 一ValueError
在这种情况下将被引发。预先警告如何
init=False
调用期间字段工作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__()
代码将调用名为 __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()
句柄 init=False
领域。
类变量¶
两个地方之一 dataclass()
实际检查字段的类型是确定字段是否是中定义的类变量 PEP 526 . 它通过检查字段类型是否为 typing.ClassVar
.如果字段是 ClassVar
将其作为字段排除在考虑范围之外,并被数据类机制忽略。这样 ClassVar
模块级不返回伪字段 fields()
功能。
仅初始化变量¶
另一个地方 dataclass()
检查类型批注是为了确定字段是否是仅初始化的变量。它通过查看字段的类型是否为 dataclasses.InitVar
. 如果字段是 InitVar
,它被认为是一个伪字段,称为仅初始化字段。因为它不是一个真字段,所以模块级不会返回它。 fields()
功能。仅init字段作为参数添加到生成的 __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)
冻结的实例¶
无法创建真正不可变的Python对象。但是,路过 frozen=True
到 dataclass()
decorator可以模拟不可变性。在这种情况下,数据类将添加 __setattr__()
和 __delattr__()
类的方法。这些方法将提高 FrozenInstanceError
当被调用时。
使用时会有很小的性能损失 frozen=True
: __init__()
不能使用简单赋值来初始化字段,并且必须使用 object.__setattr__()
.
遗传¶
当数据类由 dataclass()
decorator,它在reverse mro(也就是从 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
.
生成的 __init__()
方法 C
看起来像:
def __init__(self, x: int = 15, y: int = 0, z: int = 10):
默认工厂功能¶
如果A
field()
指定一个default_factory
,当需要字段的默认值时,使用零参数调用它。例如,要创建列表的新实例,请使用:mylist: list = field(default_factory=list)如果字段被排除在
__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
,正如预期的那样。使用数据类, if 此代码有效:
@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
. 也就是说,类的两个实例D
不指定值的x
创建类实例时,将共享x
. 因为数据类只使用普通的python类创建,所以它们也共享这种行为。数据类没有通用的方法来检测这种情况。相反,数据类将引发TypeError
如果它检测到类型的默认参数list
,dict
或set
. 这是一个局部解决方案,但它确实可以防止许多常见错误。使用默认factory函数可以创建可变类型的新实例作为字段的默认值:
@dataclass class D: x: list = field(default_factory=list) assert D().x is not D().x
例外情况¶
- exception dataclasses.FrozenInstanceError¶
当隐式定义
__setattr__()
或__delattr__()
在用定义的数据类上调用frozen=True
.