– 数据类 – Python运行时服务(Python教程)(参考资料)
dataclasses
– 数据类
源代码: Lib / dataclasses.py
这个模块提供了一个装饰器和函数来自动添加生成的特殊方法如__init__()
和__repr__()
到用户定义的类。它最初是在PEP 557 .
在这些生成的方法中使用的成员变量是使用PEP 526 类型注释。例如这个代码:
@dataclassclass 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)-
这个函数是装饰器用于将生成的特殊方法 s添加到类中,如下所述.
dataclass()
装饰师检查班级找到field
秒。一个field
被定义为具有类型注释。使用下面描述的两个例外,dataclass()
中没有任何内容检查变量注释中指定的类型.所有生成的方法中的字段顺序是它们在类定义中出现的顺序。
dataclass()
装饰者会在课堂上添加各种“dunder”方法,如下所述。如果在类上添加了任何alreadyexist方法,则行为取决于参数,如下所述。装饰器返回被调用的同一个类;没有创建新类.如果
dataclass()
仅用作没有参数的简单装饰器,它就像它具有此签名中记录的默认值一样。也就是说,dataclass()
的这三个用法是等价的:@dataclassclass C: ...@dataclass()class C: ...@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)class C: ...
dataclass()
的参数是:-
init
:如果是(默认值),则__init__()
方法将生成如果类已定义
__init__()
,则此参数为isignored。 -
repr
:如果为true(默认值),则__repr__()
方法将生成。生成的repr字符串将具有类名以及每个字段的名称和repr,按照它们在类中定义的顺序。标记为从reprare中排除的字段不包括在内。例如:InventoryItem(name="widget", unit_price=3.0, quantity_on_hand=10)
.如果类已定义
__repr__()
,则此参数为isignored。 -
eq
:如果为true(默认值),将生成__eq__()
方法。此方法按顺序将类比较为其字段的元组。比较中的两个实例都必须是相同的类型.如果类已经定义了
__eq__()
,则此参数为isignored。 -
order
:如果为true(默认为False
),__lt__()
,__le__()
,__gt__()
和__ge__()
将生成方法。这些按顺序比较该类,就好像它是其字段的元组一样。比较中的两个实例必须是相同的类型。如果order
为真且eq
为假,则ValueError
被提出如果该类已经定义了
__lt__()
,__le__()
,__gt__()
中的任何一个,或者__ge__()
,然后TypeError
被抬起来 -
unsafe_hash
:如果False
(默认),__hash__()
按照eq
和frozen
设置了__hash__()
由内置的hash()
使用,并且当对象被添加到散列集合(如字典和集合)时使用。有一个__hash__()
暗示类的实例是不可变的。可变性是一个复杂的属性,取决于程序员的意图,__eq__()
,以及eq
和frozen
中的标志dataclass()
decorator.默认情况下,
dataclass()
不会暗中添加__hash__()
方法,除非这样做是安全的。也不会添加或更改现有的明确定义__hash__()
方法。设置classattribute__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
为false,__hash__()
将保持不变意味着将使用超类的__hash__()
方法(如果超类是object
,这意味着它将回退到基于id的散列). -
frozen
:如果为true(默认值为False),分配给字段将生成异常。这模拟了只读的冻结实例。如果__setattr__()
或__delattr__()
在类中定义,然后TypeError
被引发。请参阅下面的讨论.
field
s可以选择使用normalPython语法指定默认值:@dataclassclass 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()
功能。例如:@dataclassclass C: mylist: List[int] = field(default_factory=list)c = C()c.mylist += [1, 2, 3]
如上所示,
MISSING
值是一个用于在提供default
和default_factory
参数时检测的标记对象。使用这个哨兵是因为None
是default
的有效值。没有代码应该直接使用MISSING
value.field()
的参数是:-
default_factory
:如果提供,它必须是零参数可调用,当需要该字段的默认值时将调用它。除了其他目的之外,这可以用于指定具有可变默认值的字段,如下所述。指定default
和default_factory
. -
compare
//:如果为true(默认值),则此字段包含在生成的等式和比较方法中(__eq__()
,__gt__()
,等人). -
hash
:这可以是一个bool或None
。如果为true,则该字段包含在生成的__hash__()
方法中。如果None
(默认值),请使用compare
:这通常是预期的行为。应该在用于比较的hashif中考虑一个字段。不建议将此值设置为None
以外的其他任何内容.设置
hash=False
但是compare=True
的可能原因是,如果字段计算哈希是昂贵的对于等式测试,需要该字段的值,并且还有其他字段对类型的哈希值有贡献。即使从散列中排除了一个字段,它仍将用于比较. -
metadata
:这可以是映射或无。没有人被视为空的dict。这个值包含在MappingProxyType()
中,使其成为只读,并暴露在Field
对象中。DataClasses根本不使用它,它是作为第三方扩展机制提供的。多个第三方都可以拥有自己的密钥,用作元数据中的anamespace
如果默认值为通过调用
field()
指定字段,然后该字段的class属性将被指定的default
值替换。如果不default
是提供的,那么class属性将被删除。意图是在dataclass()
如果指定了默认值,则decorator将运行,classattributes将包含字段的默认值。例如,在:@dataclassclass 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
对象直接。其记录的属性是:其他属性可能存在,但它们是私密的,不得被视察或依赖.
dataclasses.
fields
(class_or_instance)-
返回一个元组
Field
定义thisdataclass字段的对象。接受数据类或数据类的实例.RaisesTypeError
如果没有传递一个数据类或一个实例。不返回伪字段ClassVar
或InitVar
.
dataclasses.
asdict
(instance, *, dict_factory=dict)-
转换数据类
instance
到dict(使用thefactory函数dict_factory
)。每个数据类都转换为其字段的字典,如name: value
对。数据类,dicts,列表和元组被递归。例如:@dataclassclass Point: x: int y: int@dataclassclass 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
不是数据类实例.
dataclasses.
astuple
(instance, *, tuple_factory=tuple)-
将数据类
instance
转换为元组(使用thefactory函数tuple_factory
)。每个数据类都转换为其字段值的元组。数据类,词组,列表和元组被递归到.继续前面的例子:
assert astuple(p) == (10, 20)assert astuple(c) == ([(0, 0), (10, 4)],)
如果
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
,base在bases
中给出的类,并使用namespace
.fields
是aiterable,其元素分别是name
,(name, type)
或(name, type, Field)
。如果只提供name
,typing.Any
用于type
。init
,repr
,eq
,order
,unsafe_hash
和frozen
的值与dataclass()
.这个函数并不是严格要求的,因为使用
__annotations__
canthen创建一个新类的任何Python机制都适用于dataclass()
用于将该类转换为数据类的函数。提供此功能是为了方便。例如:C = make_dataclass("C", [("x", int), "y", ("z", int, field(default=5))], namespace={"add_one": lambda self: self.x + 1})
相当于:
@dataclassclass C: x: int y: "typing.Any" z: int = 5 def add_one(self): return self.x + 1
dataclasses.
replace
(instance, **changes)-
创建一个相同类型的新对象
instance
,用changes
中的值替换字段。如果instance
不是DataClass,则引发TypeError
。如果changes
中的值没有指定字段,则引发TypeError
.通过调用
__init__()
数据类的方法。这样可以确保__post_init__()
(如果存在的话)也被调用.Init-只有没有默认值的变量,如果存在,必须在调用
replace()
这样它们就可以传递到__init__()
和__post_init__()
.这是
changes
包含任何被定义为具有init=False
。一个ValueError
在这种情况下会被提出来预先警告
init=False
字段在调用replace()
时工作。它们不是从源对象复制的,而是在__post_init__()
初始化,如果它们完全没有初始化的话。预计init=False
很少和明智地使用字段。如果使用它们,它可能会有替代类构造函数,或者可能是一个习惯replace()
(或类似命名的)处理instancecopying的方法.
post–init处理
生成的__init__()
代码将调用名为__post_init__()
的方法,如果在类上定义了__post_init__()
。它通常被称为self.__post_init__()
。但是,如果定义了InitVar
字段,它们也会被通过__post_init__()
按照他们在课堂上定义的顺序。如果没有生成__init__()
方法,那么__post_init__()
将不会被自动调用.
在其他用途中,这允许初始化依赖于一个或多个其他字段的字段值。例如:
@dataclassclass 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
fields.
类变量
dataclass()
实际上检查一个字段的类型是确定一个字段是否是一个类定义的变量PEP 526 。它是通过检查字段的类型是否typing.ClassVar
。如果一个字段是ClassVar
,它被排除在考虑范围之外,并被数据类机制忽略。这样的ClassVar
伪字段不会被模块级返回fields()
功能
只能变量
的另一个地方dataclass()
检查类型注释是为了确定一个字段是否是一个init-only变量。它是通过查看字段的类型为dataclasses.InitVar
来实现的。如果一个字段是InitVar
,则它被认为是一个称为init-onlyfield的伪字段。因为它不是真正的字段,所以它不是由模块级fields()
函数返回的。仅初始化字段作为生成的__init__()
方法的参数添加,并传递给可选的__post_init__()
方法。它们不是由数据类使用的.
例如,假设一个字段将从数据库初始化,如果在创建类时没有提供avalue:
@dataclassclass 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()
将为Field
和i
返回j
对象,但不是database
.
冻结的实例
真的无法创造不可变的Python对象。然而,通过将frozen=True
传递给dataclass()
装饰者,你可以使用不可变性。在这种情况下,数据类将__setattr__()
和__delattr__()
方法添加到类中。这些方法会在调用时引起FrozenInstanceError
.
使用时frozen=True
:__init__()
不能使用简单赋值来初始化字段,并且必须使用object.__setattr__()
.
继承
创建数据类时,性能损失很小通过dataclass()
装饰器,它查看反向MRO中的所有类的基类(即,从object
开始),并且对于它找到的每个数据类,添加该基类的字段在添加了所有基类字段之后,它将自己的字段添加到有序映射中。所有生成的方法都将使用此组合的,计算的有序字段映射。由于字段按插入顺序排列,因此派生类会覆盖基类。例如:
@dataclassclass Base: x: Any = 15.0 y: int = 0@dataclassclass 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):
默认工厂函数
可变默认值
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此代码有效:
@dataclassclass 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 += elementassert D().x is D().x的代码这与使用类
C
的原始示例有同样的问题。也就是说,类的两个实例D
在创建类实例时没有指定x
的值将共享x
。因为数据类只使用普通的Python类创建,所以它们也分享这种行为。DataClasses没有通用的方法来检测这种情况。相反,如果数据类检测到类型为TypeError
或list
,dict
的默认参数,则会引发set
。这是一个部分解决方案,但它确实保护了许多常见错误.使用默认工厂函数是一种创建新的可变类型实例的方法,作为字段的默认值:
@dataclassclass D: x: list = field(default_factory=list)assert D().x is not D().x
评论被关闭。