Using a class as a data container

76,133

Solution 1

If you're really never defining any class methods, a dict or a namedtuple make far more sense, in my opinion. Simple+builtin is good! To each his own, though.

Solution 2

Background

A summary of alternative attribute-based, data containers was presented by R. Hettinger at the SF Python's 2017 Holiday meetup. See his tweet and his slide deck. He also gave a talk at PyCon 2018 on dataclasses.

Other data container types are mentioned in this article and predominantly in Python 3 documentation (see links below).

Here is a discussion on the python-ideas mailing list on adding recordclass to the standard library.

Options

Alternatives in the Standard Library

External options

  • records: mutable namedtuple (see also recordclass)
  • bunch: add attribute access to dicts (inspiration for SimpleNamedspace; see also munch (py3))
  • box: wrap dicts with dot-style lookup functionality
  • attrdict: access elements from a mapping as keys or attributes
  • fields: remove boilerplate from container classes.
  • namedlist: mutable, tuple-like containers with defaults by E. Smith
  • attrs: similar to dataclasses, packed with features (validation, converters, __slots__, etc.). See also docs on cattrs.
  • misc.: posts on making your own custom struct, object, bunch, dict proxy, etc.

Which one?

Deciding which option to use depends on the situation (see Examples below). Usually an old fashioned mutable dictionary or immutable namedtuple is good enough. Dataclasses are the newest addition (Python 3.7a) offering both mutability and optional immutability, with promise of reduced boilerplate as inspired by the attrs project.


Examples

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

Solution 3

By the way, I think Python 3.7 implemented @dataclass is the simplest and most efficient way to implement classes as data containers.

@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())

The output would be :hello

It is too similar to Scala like case class and the easiest way to use a class as a container.

Solution 4

I prefer to follow YAGNI and use a dict.

Solution 5

There is a new proposal that aims to implement exactly what you are looking for, called data classes. Take a look at it.

Using a class over a dict is a matter of preference. Personally I prefer using a dict when the keys are not known a priori. (As a mapping container).

Using a class to hold data means you can provide documentation to the class attributes.

Personally, perhaps the biggest reason for me to use a class is to make use of the IDEs auto-complete feature! (technically a lame reason, but very useful in practise)

Share:
76,133
new name
Author by

new name

I used to be a huge fan of google cloud, but quality has gone down and support is terrible. Google cloud console is buggy, and when you ask for help regarding Google bugs, they tell you to sign up for paid support. I already pay them a lot of money, and then they want me to pay more to get around google bugs. Terrible.

Updated on October 15, 2021

Comments

  • new name
    new name over 2 years

    Sometimes it makes sense to cluster related data together. I tend to do so with a dict, e.g.,

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

    One of my colleagues prefers to create a class

    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
    

    Note that we are not defining any class methods.

    I like to use a dict because I like to minimize the number of lines of code. My colleague thinks the code is more readable if you use a class, and it makes it easier to add methods to the class in the future.

    Which do you prefer and why?