Using Python class as a data container

Viewed 82563

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

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

One of my colleagues prefers to create a class

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)

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?

12 Answers

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.

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)

If one do not care about memory footprint then dict, namedtuple, dataclass or just a class with __slots__ are good choices.

But if one have to create millions of objects with a few simple attributes in the context of limited memory then there is a solution based on recordclass library:

from recordclass import make_dataclass
C = make_dataclass("C", ('a', 'b', 'c'))
c = C(1, 2, 3)

Same with a class definition:

from recordclass import dataobject
class C(dataobject):
    a:int
    b:int
    c:int    
c = C(1, 2, 3)

It has minimal memory footprint = sizeof(PyObject_HEAD) + 3*sizeof(PyObject*) bytes.

For comparison __slots__-based variant require sizeof(PyGC_Head) + sizeof(PyObject_HEAD) + 3*sizeof(PyObject*) bytes.

Since 0.15 there is an option fast_new for faster instance creation:

C = make_dataclass("C", ('a', 'b', 'c'), fast_new=True)

or

class C(dataobject, fast_new=True):
    a:int
    b:int
    c:int    

This option accelerates the instance creation twice.

Related