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.
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 just
Not sure why this is—evidently there’s something outside of
__build_class__ that’s modifying the namespace. Worth further investigation.
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.
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!