使用Python类作为数据容器
有时候,把相关的数据放在一起是很有意义的。我通常会用字典来做,比如:
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邮件列表。
选项
标准库中的替代方案
collections.namedtuple
:带属性的元组(见经典的示例)typing.NamedTuple
:可以被继承的元组(见这篇帖子,比较了它和namedtuple
)types.SimpleNamespace
:简单的类,可以选择性声明类types.MappingProxy
:只读字典enum.Enum
:相关常量的有限集合(表现得像一个类)dataclasses.dataclass
:可变的命名元组,省去默认和样板代码的类
外部选项
- 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
如果你真的从来没有定义过任何类的方法,那么我觉得用字典或者命名元组会更合适。简单又是内置的功能,真不错!不过每个人的选择不同嘛。