>>> from collections import namedtuple
>>> Time = namedtuple('Time', ['hours', 'minutes'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t = Time(hours=7, minutes=40)
>>> p = Point(x=7, y=40)
>>> p == t
True
They're still tuples. They're meant more as a tool for making an already existing tuple mess slightly more managable rather than creating a new mess.For making plain data objects take less boilerplate to create have a look at attrs: https://attrs.readthedocs.io
pr<TAB> --> print('%cursor%', %cursor%)
nt<TAB> --> namedtuple('%cursor%', ['%cursor%', …])I use a lot of classes.
It feels like Java sometimes, but it does make debugging easier...
Tradeoffs, I guess.
Class creation the very expensive in Python, so Guido van Rossum explicitly recommends against using dynamically created namedtuples: https://mail.python.org/pipermail//python-ideas/2016-April/0...
- Probably more memory efficient.
>>> import collections
>>> import sys
>>> class A:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> class B:
... __slots__ = ('x', 'y')
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> C = collections.namedtuple('C', ('x', 'y'))
>>> a = A(1, 1)
>>> b = B(1, 1)
>>> c = C(1, 1)
>>> d = {'x': 1, 'y': 1}
>>> sys.getsizeof(a) + sys.getsizeof(a.__dict__) # Plain object
344
>>> sys.getsizeof(b) + sys.getsizeof(b.__slots__) # Plain object with slots
120
>>> sys.getsizeof(c) # Named Tuple
64
>>> sys.getsizeof(d) # Dict
288
EDIT: Fixed a slew of measurement issuesEDIT2: Included size of __slots__ tuple
Proof:
def make_dicts():
res = []
for i in range(1000000):
res.append({'x': i, 'y': i})
return res
def make_tuples():
templ = namedtuple('Point', ('x', 'y'))
res = []
for i in range(1000000):
res.append(templ(i, i))
return res
from ipython_memory_usage import ipython_memory_usage as imu
imu.start_watching_memory()
used 0.2305 MiB RAM in 4.00s, peaked 0.00 MiB above current, total RAM usage 86.41 MiB
%time tuples = make_tuples()
CPU times: user 780 ms, sys: 32 ms, total: 812 ms
Wall time: 816 ms
used 109.0625 MiB RAM in 0.92s, peaked 0.00 MiB above current, total RAM usage 195.47 MiB
%time dicts = make_dicts()
CPU times: user 160 ms, sys: 32 ms, total: 192 ms
Wall time: 194 ms
used 99.5117 MiB RAM in 0.30s, peaked 0.00 MiB above current, total RAM usage 524.33 MiBIt should not be controversial that namedtuples are clearer for the reason I stated in my initial comment.
To measure real usage, you have to use another method. I use ipython_memory_usage because it's easy to use.
You can test this out easily by making a large dict and see how much your OS thinks python is using vs what sys.getsizeof is reporting.
Many times when a dict is used, it's expected to have certain attributes, and may have expected ways of interacting with those attributes. Named tuples is a simple way to formalize these, which makes them not so mysterious to you future-you, or to other developers who inherit the project 6 months down the road.