Defining a Custom Class Constructor in Python
Python gives you tremendous visibility into its internal workings. The degree to which you can pop open the hood and modify what’s happening at runtime is powerful and terrifying. The author of the venerable Flask web framework Armin Ronacher gave a fantastic talk on this topic called How Python was Shaped by Leaky Internals.
One way to take advantage of these leaky internals is by overwriting the built-in class constructor. Whenever you define a class, the function __build_class__
is called behind the scenes. __build_class__
is passed the class code (which is actually, suprisingly, a function) and the class name as arguments. The result of executing __build_class__
is your class being created in the appropriate namespace. I’m simplifying, but bear with me.
Investigating __build_class__
To see this in action, let’s define a simple class called Dog
and observe __build_class__
do its job.
Now let’s define a custom build class function to see a bit of what’s happening under the hood.
You can see the two arguments that are passed to __build_class__
are the Dog
function (the code of the Dog
class) and the name of the class, 'Dog'
. Because we don’t ever call the “real” __build_class__
, the class is never defined in the namespace. Curiously, however, accessing the variable Dog
doesn’t throw NameError
. Instead, it’s justNone
.
Not sure why this is—evidently there’s something outside of __build_class__
that’s modifying the namespace. Worth further investigation.
Redefining __build_class__
Now let’s redefine the built-in class constructor so that no matter what class you define, the __build_class__
function assigns the code for Dog
to that class name instead of the code of the class you’re defining.
Chaos reigns!
Conclusion
Aside from impressing your friends and coworkers with verboten Python magic, why would you ever want to do any of this? I actually can’t think of a legitimate reason. Perhaps you need a hook that registers every class upon its creation? However, this case would probably be better served with a metaclass. If you can think of a legitimate real-world scenario for overriding __build_class__
, let me know.
This is the second in a series of blog entries I’m calling “baby snakes”, bits of Python arcana aimed at intermediate and advanced Python developers. For a similar exercise in Python’s built-in abuse, see Redefining Python’s Print() Function and check back soon for even more!