namedtuple is a class factory that takes a type name and a list of attributes and creates a class out of it. The class can then be used to instantiate a tuple-like object and also provides accessors for its elements, as follows:
>>> from collections import namedtuple >>> Customer = namedtuple( ... 'Customer', ... 'firstname lastname' ... ) >>> c = Customer('Tarek', 'Ziadé') >>> c.firstname 'Tarek'
As shown in the preceding example, it can be used to create records that are easier to write compared to a custom class that may require boilerplate code to initialize values. On the other hand, it is based on tuple, so gaining access to its elements by index is a quick process. The generated class can also be sub-classed to add more operations.
The advantage of using namedtuple over other data types may not be obvious at first. The main advantage is that it is easier to use, understand, and interpret than ordinary tuples. Tuple indexes don't carry any semantics, so it is great to be able to access tuple elements by attributes. Note that you could also get the same benefits from dictionaries that have a O(1) average complexity of get and set operations.
The first advantage in terms of performance is that namedtuple is still a flavor of tuple. This means that it is immutable, so the underlying array storage is allocated for the necessary size. Dictionaries, on the other hand, need to use overallocation in the internal hash table to ensure the low-average complexity of get/set operations. So, namedtuple wins over dict in terms of memory efficiency.
The fact that namedtuple is based on tuple may also be beneficial for performance. Its elements may be accessed by an integer index, as in other simple sequence objects-lists and tuples. This operation is both simple and fast. In the case of dict or custom class instances that use dictionaries for storing attributes, the element access requires a hash table lookup. It is highly optimized to ensure good performance independently from collection size, but as mentioned, O(1) complexity is actually only considered an average level of complexity. The actual, amortized worst-case complexity for set/get operations in dict is O(n). The real amount of work required to perform such an operation is dependent on both collection size and history. In sections of code that are critical for performance, it may be wise to use lists or tuples over dictionaries, as they are more predictable. In such a situation, namedtuple is a great type that combines the following advantages of dictionaries and tuples:
- In sections where readability is more important, the attribute access may be preferred
- In performance-critical sections, elements may be accessed by their indexes
That said, when the solution is not obvious, you should consider dropping and rewriting the incriminating part instead of killing the code's readability for the sake of performance.
Often, Python code can be both readable and fast, so try to find a good way to perform the work instead of trying to work around a flawed design.
In the next section, we'll look at how we can use architectural trade-offs.