Dynamic Attributes in Python
One of the strengths of a dynamic language is that it allows you to more easily work introspection and light weight meta-programming into your every day code. In Python, one of the primary ways of taking advantage of the dynamic nature of the language is through attribute access.
Note: this is part one in a series of posts about basic Python functionality.
In most cases, to get an attribute of an object, you just want to use obj.field
. But if you don’t know the name of the field until runtime, you can use getattr(obj, 'field')
.
This is a fairly common pattern, so you can avoid the extra try/catch and use the third default
parameter:
Both attribute access methods are virtually identical in terms of performance. The regular method produces slightly cleaner code, so normally you would use that. Besides, when do you NOT know the names of the fields you want to access ahead of time?
If you’re dealing with data, you don’t always know. For example, say you’re mapping URLs to view methods. If the user hits /user/123/settings
, you could route that to a view function as follows:
Of course, you could always do this with a pre-defined set of URLs, but the point is that you have a built-in way to avoid that code duplication. In general, this is known as keeping your code DRY. For example, notice the duplication of tokens in code like the following:
Instead, you could do something like the following:
While this is certainly more code, for a larger number of lines, there will be a code savings. It’s also easy to refactor all the obj.field lines at once, if for example you need to change it to obj.field.set(value)
.
You can also make use of dynamic attributes on the class side by over-riding __getattr__
.
There is an alternate magic method called __getattribute__
that fires even for attributes that are already declared. But be careful, it’s easy to get into an infinite recursion loop here.
This is a trivial example, better implemented with decorators. But that is a subject for another post!
Finally, there is a sister to getattr
called setattr
. As you would expect, this will set attributes by name. Here is a quick example: