使用Python类作为数据容器

gae*_*fan 48 python struct dictionary class

有时将相关数据聚集在一起是有意义的.我倾向于用dict这样做,例如,

self.group = dict(a=1, b=2, c=3)
print self.group['a']
Run Code Online (Sandbox Code Playgroud)

我的一位同事更喜欢创建一个班级

class groupClass(object):
    def __init__(a, b, c):
        self.a = a
        self.b = b
        self.c = c
self.group = groupClass(1, 2, 3)
print self.group.a
Run Code Online (Sandbox Code Playgroud)

请注意,我们没有定义任何类方法.

我喜欢使用dict,因为我喜欢最小化代码行数.我的同事认为如果使用类,代码更具可读性,并且将来更容易向类中添加方法.

你更喜欢哪个?为什么?

Joe*_*ton 45

如果你真的没有定义任何类方法,在我看来,dict或者命名元组更有意义.简单+内置很好!但是,他自己.

  • namedtuple很棒; 只记得他们是不变的.并且不要忘记如果以后需要添加类方法,您始终可以继承namedtuple的结果.例如`class Point(namedtuple('Point','x y')):...` (13认同)
  • 如何在OP代码中使用`namedtuple`可能很好. (5认同)
  • 在Python 3.7中有@dataclass装饰器,它解决了与namedtuples和其他以前的替代方案有关的一些问题. (2认同)

pyl*_*ang 29

背景

R. Hettinger在SF Python 2017年假日聚会上介绍了基于属性的替代数据容器的摘要.看他的推文和他的幻灯片.他还在PyCon 2018上就数据类进行了演讲.

本文中提到了其他数据容器类型,主要是Python 3文档(请参阅下面的链接).

以下是关于添加到标准库的python-ideas邮件列表的讨论recordclass.

选项

标准库中的替代品

外部选项

哪一个?

决定使用哪个选项取决于具体情况(参见下面的示例).通常一个老式的可变字典或不可变的namedtuple就足够了.数据类是最新增加的(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)
Run Code Online (Sandbox Code Playgroud)

# 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>
Run Code Online (Sandbox Code Playgroud)

# 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
Run Code Online (Sandbox Code Playgroud)


Ign*_*ams 8

我更喜欢跟随YAGNI并使用词典.


nes*_*dis 7

有一个新提案旨在实现您正在寻找的内容,称为数据类.看看吧.

在dict上使用类是一个偏好的问题.就个人而言,我更喜欢使用一个字典,当钥匙不是先验的时候.(作为映射容器).

使用类来保存数据意味着您可以为类属性提供文档.

就个人而言,也许我使用类的最大原因是使用IDE自动完成功能!(技术上是一个蹩脚的原因,但在实践中非常有用)

  • 该提案现已在 Python 3.7 中正式发布 (2认同)

小智 6

您可以使用从 dict 继承的一些包装类,将 dict 和 class 的优点结合在一起。您不需要编写样板代码,同时可以使用点表示法。

class ObjDict(dict):
    def __getattr__(self,attr):
        return self[attr]
    def __setattr__(self,attr,value):
        self[attr]=value

self.group = ObjDict(a=1, b=2, c=3)
print self.group.a
Run Code Online (Sandbox Code Playgroud)


jam*_*ndu 6

顺便说一句,我认为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())
Run Code Online (Sandbox Code Playgroud)

输出将是:hello

它与Scala之类的案例类太相似,也是将类用作容器的最简单方法。

  • 我不知道“最有效”是否正确,因为它们具有相当大的内存占用量。他们无疑是“更方便”的。 (2认同)

Muh*_*uri 5

你的方法更好。不要试图对未来太过期望,因为您不太可能成功。

但是,有时候使用诸如C struct之类的东西可能是有意义的,例如,如果您要标识不同的类型而不是对所有内容使用字典。


int*_*ath 5

如果不关心内存占用,那么dictnamedtupledataclass或只是一个类__slots__都是不错的选择。

但是,如果必须在有限内存的情况下创建数百万个具有一些简单属性的对象,那么有一种基于recordclass库的解决方案:

from recordclass import make_dataclass
C = make_dataclass("C", ('a', 'b', 'c'))
c = C(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

与类定义相同:

from recordclass import dataobject
class C(dataobject):
    a:int
    b:int
    c:int    
c = C(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

它具有最小的内存占用=sizeof(PyObject_HEAD) + 3*sizeof(PyObject*)字节。

对于__slots__基于比较的变体需要sizeof(PyGC_Head) + sizeof(PyObject_HEAD) + 3*sizeof(PyObject*)字节。

从 0.15 开始,有一个选项fast_new可以加快实例创建速度:

C = make_dataclass("C", ('a', 'b', 'c'), fast_new=True)
Run Code Online (Sandbox Code Playgroud)

或者

class C(dataobject, fast_new=True):
    a:int
    b:int
    c:int    
Run Code Online (Sandbox Code Playgroud)

此选项可将实例创建速度加快两倍。