Snake_Byte #18: namedtuples with defaults

I would estimate namedtuple to be the most commonly used container type in Python's supplemental collections module, and it doesn't take long for new Python developers to add it to their toolbelts. Though I've used namedtuples for years, I recently learned a method to make namedtuples a bit more versatile.

The key advantage to a namedtuple, of course, is that its elements can be accessed by name. However, instantiation of a namedtuple is not much different from a raw tuple. Consider the following representations of RGBA color values:

In both cases, the value for each RGBA channel must be given - there are no defaults. Namedtuple does not provide an explicit mechanism to define default values for its fields, but it is possible by taking advantage of Python's object model. Let's just skip to the payoff and then dig into how it works:

This technique can help clean up your namedtuple usage, if it makes sense to assign default values to whatever you are representing. I have also found it to be a useful technique when writing tests for functions that take a lengthy namedtuple as input, but only one or two fields are relevant.

Now let's examine how this works. The __defaults__ attribute on user-defined functions is simply a "tuple containing default argument values for those arguments that have defaults, or None if no arguments have a default value." See the Python internal data model documentation. Here we are setting default argument values for the Color class' __new__ method, that is the object creation method. The namedtuple implementation - an illuminating read by the way - generates a class with a __new__ method that instantiates the underlying tuple instance with the user-supplied positional arguments. Unless overridden, our default argument values are passed along.

You may wonder why we are able to use keyword arguments when our __new__ method is defined with positional arguments. In fact, positional arguments for any Python function - or more generally any callable - can be passed as you would with keyword arguments. So, with the original definition of Color (without defaults), you could do this:

However, it would be good practice to either specify all or none of the positional arguments by keyword, as order matters when you mix positional and keyword arguments. For instance, if you attempt to do the following, the Python interpreter will complain at you about positional arguments appearing after a keyword argument:

One last tip for setting defaults on a namedtuple: If you want to set all arguments to the same default, say None, you can use this shorthand:

This technique of setting defaults for a function in Python after its definition is only situationally advisable. With it you could create some wonderfully obfuscated code, but as demonstrated here, it has the ability to help make your code more concise and legible.