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.

class Dog:
  def __init__(self):
    print('bark bark')

dog = Dog()
#> bark bark

Now let’s define a custom build class function to see a bit of what’s happening under the hood.

def custom_build_class(*args):
  print(args)

__builtins__.__build_class__ = custom_build_class

class Dog:
  pass
#> (<function Dog at 0x10f6ff6a8>, 'Dog')

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.

print(Dog)
#> None

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.

class Dog:
  def __init__(self):
    print('woof woof')

def custom_build_class(*args):
  return Dog

__builtins__.__build_class__ = custom_build_class

class Cat:
  def __init__(self):
    print('meow meow')

cat = Cat()
#> bark bark

print(type(cat))
#> <class '__main__.Dog'>

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!