如何使用Python数据类库


如何使用Python数据类库

Python 中的每样东西都是一个对象,这句话是这么说的。如果你想创建你自己的自定义对象,有自己的属性和方法,你可以使用 Python 的类对象来实现这一点。但是在 Python 中创建类有时意味着要写大量重复的、模板式的代码,以便从传递给它的参数中设置类实例,或者创建常见的函数,如比较运算符。

在 Python 3.7 中引入的数据类 (并回传到 Python 3.6),提供了一种方便的方法,使类不再那么冗长。许多你在类中做的普通事情,比如从传递给类的参数中实例化属性,都可以简化为一些基本的指令。

Python 数据类的例子

下面是一个简单的 Python 传统类的例子:

class Book: '''用于跟踪集合中物理书籍的对象。'' def __init__(self, name: str, weight: float, shelf_id: int = 0) : self. name = name self.weight = weight # 以克为单位,用于计算运输 self.shelf_id = shelf_id def __repr__(self): return(f "Book(name={self.name!r}, weight={self.weight!r}, shelf_id={self.shelf_id!r}")

这里最头痛的是传递给 __init__ 的每个参数必须被复制到对象的属性中。如果你只处理 Book,这还不算太糟,但如果你必须处理 Bookshelf、Library、Warehouse 等,那该怎么办?另外,你必须手工输入的代码越多,你犯错误的机会就越大。

下面是同一个 Python 类,作为一个 Python 数据类实现:

f from dataclasses import dataclass@dataclassclass Book: '''用于跟踪集合中物理书籍的对象。'' name: str weight: float shelf_id: int = 0

当你在一个数据类中指定属性,称为域,@dataclass 自动生成初始化它们所需的所有代码。它还保留了每个属性的类型信息,所以如果你使用像 mypy 这样的代码转换器,它将确保你向类的构造函数提供正确的变量类型。

@dataclass 在幕后做的另一件事是自动为类中的一些常见错误方法创建代码。在上面的传统类中,我们必须创建我们自己的 __repr__。在数据类中,这是不必要的;@dataclass 为你生成了 __repr__。

一旦一个数据类被创建,它在功能上与普通类是相同的。使用数据类没有任何性能上的损失,除了在声明类定义时装饰器的最小开销。

用字段函数定制 Python 数据类字段

默认的数据类工作方式对于大多数的使用情况来说应该是可以的。但有时,你需要微调你的数据类中的字段如何被初始化。

f from dataclasses import dataclass, fieldfrom typing import List@dataclassclass Book: '''Object for tracking physical books in a collection.' name: str condition: str = field(compare=False) weight: float = field(default=0.0, repr=False) shelf_id: int = 0 chapters: List[str] = field(default_factory=list)

当你给field的一个实例设置默认值时,它会根据你给field的参数来改变field的设置方式。这些是field最常用的选项(还有其他的):

default。为该字段设置默认值。你需要使用default,如果你a)使用field来改变字段的任何其他参数,以及b)你想在字段上设置一个默认值。在本例中,我们使用default来设置权重为0.0.default_factory。提供一个函数的名称,该函数不需要任何参数,返回一些对象作为字段的默认值。在这种情况下,我们希望chapters是一个空列表。repr:默认情况下(True),控制有关字段是否显示在数据类的自动生成的__repr__中。在本例中,我们不希望书的重量显示在__repr__中,所以我们使用repr=False来省略它。默认情况下(True),在为数据类自动生成的比较方法中包括该字段。在这里,我们不希望 condition 被用作两本书比较的一部分,所以我们设置 compare=False。

注意,我们不得不调整字段的顺序,使非默认字段排在前面。

使用 __post_init__ 来控制 Python 数据类的初始化

在这一点上你可能想知道。如果一个数据类的 __init__ 方法是自动生成的,那么我怎样才能控制初始化过程,以进行更精细的修改呢?

进入 __post_init__ 方法。如果你在你的数据类定义中包含 __post_init__ 方法,你可以提供修改字段或其他实例数据的指令。

f from dataclasses import dataclass, fieldfrom typing import List@dataclassclass Book: ''''Object for tracking physical books in a collection.' name: str weight: float = field(default=0.0, repr=False) shelf_id: int = field(init=False) chapters: List[str] = field(default_factory=list) condition: str = field(default="Good", compare=False) def __post_init__(self): if self.condition == "Discarded": self.shelf_id = None else: self.shelf_id = 0

在这个例子中,我们创建了一个 __post_init__ 方法来设置 shelf_id 为 None,如果书的条件被初始化为 "Discarded"。注意我们如何使用field来初始化shelf_id,并将init作为False传递给field。这意味着 shelf_id 不会在 __init__ 中被初始化。

使用 InitVar 来控制 Python 数据类的初始化

另一种定制 Python 数据类设置的方法是使用 InitVar 类型。这让你可以指定一个字段,它将被传递给 __init__,然后传递给 __post_init__,但不会被存储在类的实例中。

通过使用 InitVar,你可以在设置数据类时加入只在初始化时使用的参数。一个例子:

from dataclasses import dataclass, field, InitVarf from typing import List@dataclassclass Book: '''Object for tracking physical books in a collection.' name: str condition: InitVar[str] = None weight: float = field(default=0.0, repr=False) shelf_id: int = field(init=False) chapters: List[str] = field(default_factory=list) def __post_init__(self, condition): if condition == "Discarded": self.shelf_id = None else: self. shelf_id = 0

将一个字段的类型设置为 InitVar (它的子类型是实际的字段类型),向 @dataclass 发出信号,不要将该字段变成一个数据类字段,而是将数据作为一个参数传递给 __post_init__。

在这个版本的 Book 类中,我们没有将 condition 存储为类实例的一个字段。我们只在初始化阶段使用 condition。如果我们发现 condition 被设置为 "Discarded",我们将 shelf_id 设置为 None - 但我们并没有将 condition 存储在类实例中。

何时使用 Python 数据类 - 以及何时不使用它们

使用数据类的一个常见场景是作为 namedtuple 的替代。数据类提供了相同的行为和更多的内容,并且它们可以通过简单地使用 @dataclass(frozen=True) 作为装饰器而变得不可改变 (就像命名图元那样)。

另一个可能的使用情况是用数据类的嵌套实例来取代嵌套的字典,这可能是笨拙的工作。如果你有一个数据类 Library,有一个列表属性的书架,你可以使用一个数据类 ReadingRoom 来填充这个列表,然后添加一些方法来使它容易访问嵌套的项目 (例如,某个房间的书架上的书)。

但是不是每个 Python 类都需要成为一个数据类。如果你创建一个类主要是为了把一堆静态方法组合在一起,而不是作为数据的容器,你就不需要把它变成一个数据类。例如,解析器的一个常见模式是有一个类,它接收一个抽象的语法树,行走该树,并根据节点类型分配对该类中不同方法的调用。因为解析器类自身的数据很少,所以数据类在这里并不有用。

如何用Python做更多事情在Python中开始使用async如何在Python中使用asyncio如何使用PyInstaller来创建Python的可执行文件Cython教程。如何加快Python的速度如何以聪明的方式安装Python如何用Poetry管理Python项目如何用Pipenv管理Python项目Virtualenv和venv。Python虚拟环境解释Python virtualenv和venv的注意事项Python线程和子进程解释如何使用Python调试器如何使用timeit对Python代码进行剖析如何使用cProfile对Python代码进行剖析如何将Python转换为JavaScript(然后再返回)。


本文标签

热门标签

会员评论