ZetCode

Python dataclass 装饰器

最后修改于 2024 年 1 月 29 日

Python dataclass 教程展示了如何在自定义类中使用 Python 的 dataclass 装饰器。dataclass 装饰器有助于减少一些样板代码。

Python dataclass 装饰器

dataclass 装饰器用于自动为类生成特殊方法,包括 __str____repr__。它有助于减少一些样板代码。dataclass 装饰器位于 dataclasses 模块中。

dataclass 装饰器会检查类以查找字段。字段定义为具有类型注解的类变量。

@dataclass
class Test:
...

@dataclass()
class Test:
...

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Test:
...

这三个声明是等效的。如果在装饰器中未设置参数,则会使用默认参数。如果 init 参数设置为 True,则会生成 __init__ 方法。如果类已定义 __init__,则忽略该参数。如果 repr 参数设置为 True,则会生成 __repr__ 方法。如果类已定义 __repr__,则忽略该参数。如果 eq 参数设置为 True,则会生成 __eq__ 方法。如果类已定义 __eq__,则忽略该参数。

如果 order 参数设置为 True,则会生成 __lt____le____gt____ge__ 方法。如果类已定义任何这些方法,则会引发 ValueError。如果 unsafe_hash 定义为 False,则会根据 eqfrozen 的设置生成 __hash__ 方法。如果 frozen 参数设置为 True,则对字段的赋值将引发异常。

Python 普通自定义类

在普通的自定义 Python 类中,我们会手动提供构造函数和其他方法,如 __repr__

regular_class.py
#!/usr/bin/python

class Person:

    def __init__(self, name, age):

        self.name = name
        self.age = age

    def __repr__(self):

        return f'Person{{name: {self.name}, age: {self.age}}}'


p = Person('John Doe', 34)
print(p)

该示例显示了一个 Person 类,它有一个构造函数和 __repr__ 方法,该方法提供了对象的完整表示。

$ ./regular_class.py
Person{name: John Doe, age: 34}

Python dataclass 示例

以下示例展示了 dataclass 装饰器的简单用法。

simple_dataclass.py
#!/usr/bin/python

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

p = Person('John Doe', 34)
print(p)

我们有一个包含两个字段的类:namestr

from dataclasses import dataclass

dataclass 装饰器位于 dataclasses 模块中。

@dataclass
class Person:
    name: str
    age: int

我们将 dataclass 装饰器应用于 Person 类。

p = Person('John Doe', 34)
print(p)

创建了一个新的 person 对象。它的 __init__ 方法被调用,该方法由 dataclass 装饰器自动生成。

$ ./simple_dataclass.py
Person(name='John Doe', age=34)

Python dataclass 默认值

可以为字段提供默认值。

default_values.py
#!/usr/bin/python

from dataclasses import dataclass

@dataclass
class Person:
    name: str = 'unknown'
    age: int = 0

p = Person('John Doe', 34)
print(p)

p2 = Person()
print(p2)

在该示例中,Person 类有两个字段;这些字段具有一些默认值。

@dataclass
class Person:
    name: str = 'unknown'
    age: int = 0

使用赋值运算符 (=),我们为字段提供默认值。

p2 = Person()
print(p2)

当我们在构造函数中不提供值时,字段将具有默认值。

$ ./default_values.py
Person(name='John Doe', age=34)
Person(name='unknown', age=0)

dataclass frozen 参数

如果 frozen 参数设置为 True,则无法为字段赋值。

frozen.py
#!/usr/bin/python

from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
    name: str
    age: int

p = Person('John Doe', 34)
p.occupation = 'gardener'

print(p)
print(p.occupation)

在该示例中,frozen 参数设置为 True。程序将因以下错误消息而失败:dataclasses.FrozenInstanceError: cannot assign to field 'occupation'

dataclass asdict 函数

asdict 函数将 dataclass 实例转换为其字段的字典。

as_dict_fun.py
#!/usr/bin/python

from dataclasses import dataclass, asdict

@dataclass
class Person:
    name: str
    occupation: str
    age: int

p = Person('John Doe', 'gardener', 34)
print(p)

print(asdict(p))

在该示例中,我们在 asdict 函数的帮助下打印 Person 类的字段。

$ ./as_dict_fun.py
Person(name='John Doe', occupation='gardener', age=34)
{'name': 'John Doe', 'occupation': 'gardener', 'age': 34}

第一行是 __repr__ 方法的输出。第二行是字段的字典。

dataclass field 函数

使用 field 函数,我们可以提供一些额外的每个字段信息。

fields.py
#!/usr/bin/python

from dataclasses import dataclass, field

@dataclass
class Person:
    name: str
    age: int
    occupation: str = field(init=False, repr=False)

p = Person('John Doe', 34)
print(p)

p.occupation = "Gardener"
print(f'{p.name} is a {p.occupation}')

在该示例中,我们有一个额外的 occupation 字段。

occupation: str = field(init=False, repr=False)

occupation 字段未包含在 __init____repr__ 方法中。

$ ./fields.py
Person(name='John Doe', age=34)
John Doe is a Gardener

Python dataclass 和模式匹配

下一个示例使用数据类和模式匹配语法。

points.py
#!/usr/bin/python

from dataclasses import dataclass


@dataclass
class Point:
    x: int
    y: int


def check(p):
    match p:
        case Point(x=0, y=0):
            print("Origin")
        case Point(x, y) if y == 0:
            print(f"on x axis")
        case Point(x, y) if x == 0:
            print(f"on y axis")
        case Point(x, y) if x > 0 and y > 0:
            print("Q I")
        case Point(x, y) if x < 0 and y > 0:
            print("Q II")
        case Point(x, y) if x < 0 and y < 0:
            print("Q III")
        case Point(x, y) if x > 0 and y < 0:
            print("Q IV")
        case _:
            print("Not a point")


points = [Point(3, 0), Point(0, 0), Point(-4, -5), Point(-4, 0), Point(0, 5),
          Point(4, 8), Point(-5, 3), Point(6, -4)]

for p in points:
    check(p)

我们有一个 Point 对象列表。使用 match/case 关键字,我们将每个点分配给原点、x 轴和 y 轴,或者四个象限之一。

case Point(x=0, y=0):
    print("Origin")

在此 case 语句中,我们匹配一个 x=0 且 y=0 坐标的点。

case Point(x, y) if x > 0 and y > 0:
    print("Q I")

使用 guard,我们检查点是否位于第一象限。

$ ./points.py 
on x axis
Origin
Q III
on x axis
on y axis
Q I
Q II
Q IV

来源

Python 数据类 - 语言参考

在本文中,我们学习了 Python dataclass 装饰器。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Python 教程