使用Python类作为数据容器

74 投票
12 回答
94827 浏览
提问于 2025-04-16 01:59

有时候,把相关的数据放在一起是很有意义的。我通常会用字典来做,比如:

group = dict(a=1, b=2, c=3)
print(group['a'])

而我的一个同事更喜欢创建一个类

class groupClass:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

group = groupClass(1, 2, 3)
print(group.a)

注意,我们并没有定义任何类的方法。

我喜欢用字典,因为我想让代码行数尽量少。我的同事觉得用类写的代码更容易读懂,而且将来如果需要添加方法也更方便。

你更喜欢哪种方式,为什么呢?

12 个回答

11

顺便说一下,我觉得Python 3.7引入的@数据类(dataclass)是实现类作为数据容器的最简单和最高效的方法。

@dataclass
class Data:
    a: list
    b: str    #default variables go after non default variables
    c: bool = False

def func():
    return A(a="hello")

print(func())

输出结果是:hello

它和Scala中的case class非常相似,是使用类作为容器的最简单方法。

66

背景

R. Hettinger在2017年SF Python的假日聚会上介绍了一些基于属性的数据容器的替代方案。你可以查看他的推特和他的幻灯片。他还在2018年的PyCon上做了一个关于数据类的演讲

其他数据容器类型在这篇文章中提到,主要在Python 3的文档中也有介绍(见下面的链接)。

这里有关于在标准库中添加recordclass的讨论,来自python-ideas邮件列表。

选项

标准库中的替代方案

外部选项

  • records:可变的命名元组(也可以看看recordclass
  • bunch:给字典添加属性访问(启发了SimpleNamedspace;还可以看看munch(py3))
  • box:用点风格查找功能包装字典
  • attrdict:可以通过键或属性访问映射中的元素
  • fields:去掉容器类中的样板代码。
  • namedlist:可变的、类似元组的容器,带有默认值,由E. Smith开发
  • attrs:类似于数据类,功能丰富(验证、转换器、__slots__等)。还可以查看cattrs的文档。
  • 杂项:关于自定义结构、对象、bunch、字典代理等的帖子。

选择哪个?

选择使用哪个选项取决于具体情况(见下面的示例)。通常,一个传统的可变字典或不可变的命名元组就足够了。数据类是最新的添加(Python 3.7a),提供了可变性和可选的不可变性,并承诺减少样板代码,这受到attrs项目的启发。


示例

import typing as typ
import collections as ct
import dataclasses as dc


# Problem: You want a simple container to hold personal data.
# Solution: Try a NamedTuple.
>>> class Person(typ.NamedTuple):
...     name: str
...     age: int
>>> a = Person("bob", 30)
>>> a
Person(name='bob', age=30)
# Problem: You need to change age each year, but namedtuples are immutable. 
# Solution: Use assignable attributes of a traditional class.
>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
>>> b = Person("bob", 30)
>>> b.age = 31
>>> b
<__main__.Person at 0x4e27128>
# Problem: You lost the pretty repr and want to add comparison features.
# Solution: Use included repr and eq features from the new dataclasses.
>>> @dc.dataclass(eq=True)
... class Person:
...     name: str
...     age: int
>>> c = Person("bob", 30)
>>> c.age = 31
>>> c
Person(name='bob', age=31)
>>> d = Person("dan", 31)
>>> c != d
True
57

如果你真的从来没有定义过任何类的方法,那么我觉得用字典或者命名元组会更合适。简单又是内置的功能,真不错!不过每个人的选择不同嘛。

撰写回答