$ ls ~yifei/notes/

Python 3 中的 dataclass

Posted on:

Last modified:

2022 更新

建议使用 Pydantic,用过之后就再也不想用 dataclass 了!


在 Python 中,如果要为一个类添加一些数据成员的话,需要做的事情还挺多,比如说编写 __init__, __str__ 这些函数,代码都是重复的,没啥意义。在 Python 3.7 中,终于添加了一个语法糖,叫做 dataclass. 下面我们就来看一下吧~

# 注意!包名叫 dataclasses, 多了个 es
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

上面的代码就相当于以前的:

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

def __repr__(self):
    ...

def __eq__(self):
    ...

...

我们知道,在 Python 的默认参数中,使用 mutable(可变) 的对象是一种常见的坑,在 dataclass 中当然也存在了,还好标准库中给我们提供了一个方法。

# 会报错
@dataclass
class Request:
    headers: dict = {}

# 相当于
class Request:
    def __init__(self, headers={}):
        self.headers = headers

# 正确的写法
from dataclasses import field

@dataclass
class Request:
    headers: dict = field(default_factory=dict)

字典这种类型是比较容易想起来不能直接做参数的,比较坑的是对于其他的自定义对象,Python 解释器并不会提示有问题,比如说这样:

# 千万别这么做
@dataclass
class Request:
    headers: Headers = Headers()

这时候坑爹的事情就发生了,每次创建新的 Request 对象引用的都是同一个 Headers 对象,也就是 在声明这个类的同时产生的这个 Headers 对象!原因也很简单,就像是上面的 dict 一样,这个 Headers 并不是在 init 函数中,所以只会产生一次。所以,需要牢记的是:dataclass 中的所有 对象默认值就相当于函数的默认参数,永远不要传递一个 mutable 就好了。

# 上面的例子相当于
class Request:
    def __init__(self, headers=Headers()):
        self.headers = headers

# 正确的做法
@dataclass
class Request:
    headers: Headers = field(default_factory=Headers)

dataclasses 模块中还提供了 asdict 方法,这样就可以方便地转换为 json 对象啦。

from dataclasses import asdict

@dataclass
class Request:
    url: str = ""
    method: str = ""

req = asdict(Request())

参考资料

  1. https://docs.python.org/3/library/dataclasses.html
WeChat Qr Code

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.

友情链接: MySQL 教程站