Python: Declaring Dynamic Attributes

The examples below are in Python 3.5, but otherwise this post is applicable to both Python 2.x and 3.x

It is a common solution in Python to override the __getattr__ magic method in classes, to implement dynamic attributes. Consider AttrDict - it is a dictionary that allows attribute-like access to its stored key-value pairs:

class AttrDict(dict):
    def __getattr__(self, item):
        return self[item]

This simplified AttrDict allows to read dictionary values from it like attributes, but it’s pretty simple to also allow to set key-value pairs. At any rate, here it is in action:

>>> attrd = AttrDict()
... attrd["key"] = "value"
... print(attrd.key)

Overriding __getattr__ (and __setattr__) is very useful - from simple “gimmicks” like AttrDict that makes your code more readable to essential building blocks of your application like remote procedure callers (RPCs). There is, however, something a little bit frustrating about dynamic attributes - you can’t see them before you use them!

Dynamic attributes have two usability problems when working in an interactive shell. The first is that they don’t appear when a user tries to examine the object’s API by calling the dir method:

>>> dir(attrd)  # I wonder how I can use attrd
['__class__', '__contains__', ... 'keys', 'values']
>>> # No dynamic attribute :(

The second problem is autocompletion - if we set normal_attribute as an attribute the old fashioned way, we get autocompletion from most modern shell environments1:

But setting dynamic_attribute by inserting it as a dictionary key-value pair does not provide us with autocompletion:

There is, however, an extra step you can take when implementing dynamic attributes which will make it a delight for your users and kill two birds with one stone - implement the __dir__ method. From the docs:

If the object has a method named __dir__(), this method will be called and must return the list of attributes. This allows objects that implement a custom __getattr__() or __getattribute__() function to customize the way dir() reports their attributes.

Implementing __dir__ is straightforward: return a list of attribute names for the object:

class AttrDict(dict):
    def __getattr__(self, item):
        return self[item]

    def __dir__(self):
        return super().__dir__() + [str(k) for k in self.keys()]

This will make dir(attrd) return dynamic attributes as well as the regular ones. The interesting thing about this is that shell environments often use __dir__ to suggest autocompletion options! so without any additional effort, we also get autocompletion2:

  1. Shell screenshots are from IDLE. ↩︎

  2. “Autoautocompletion” ↩︎

Discuss this post at Hacker News, /r/Programming, or the comment section below.
Follow me on Twitter and Facebook
Thanks to Ram Rachum for reading drafts of this.

Similar Posts