Pythonic MetaProgramming With MetaClasses | by Emmett Boudreau | Towards Data Science

Metaprogramming is the construction of code to manipulate code and can allow for some pretty interesting and expressive results in syntax. Metaprogramming is not a concept that is typically associated with Python, but the reality is that Python and meta actually go together in a lot of cool and unique ways. If you’ve ever worked with metaclasses or decorators in Python, then you were actually metaprogramming in Python.
Like most high-level programming languages, in Python, every individual variable or object has a type. Types are important because they determine how data is stored in memory for each variable. Every type in Python is defined by a class, and we can illustrate this by using the type() function in the standard library:
string = "example"print(type(string))<class 'str'>
The class for the string type is the “ str” class, which contains methods like split() which are typically used to parse chars. This is likely not a new concept for any Python programmer, but we can take this one step further by using metaclasses to manipulate classes without ever writing in them.
Unlike in C, Java, or even C++ where int, char, and float are the primary datatypes, in Python, they are an object in a class. This works both to Python’s advantage and Python’s disadvantage, as PyObjects are datatypes that aren’t typically readable universally. This means that often data can end up stuck in Python, but it also means that data used in Python is an instance of a class, which is an instance of a metaclass.
The first thing that we’ll need is a decorator. A decorators job in Python is to create a design pattern that will allow us to add new functionality to our classes without modifying them. Using decorators will make it incredibly easy to keep our classes static while still adding additional features as needed. I like to make function decorators for my class decorators to inherit because it creates a greater boundary between our code, which we are trying to avoid mutating.
from functools import wrapsdef debug(func):  '''decorator; debugs function when passed.'''  @wraps(func)  def wrapper(*args, **kwargs):   print("Full name of this method:", func.__qualname__)   return func(*args, **kwargs)  return wrapper
Now we can add our actual class decorator:
def debugmethods(cls):  '''class decorator to use debug method'''  for key, val in vars(cls).items():   if callable(val):    setattr(cls, key, debug(val))  return cls
And consequently, our metaclass:
class debugMeta(type):  '''meta class which feed created class object  to debugmethod to get debug functionality  enabled objects'''  def __new__(cls, clsname, bases, clsdict):   obj = super().__new__(cls, clsname, bases, clsdict)   obj = debugmethods(obj)   return obj 
Now we can inherit this new class’s data by creating a new class using the parameter (metaclass=).
# now all the subclass of this # will have debugging applied class Base(metaclass=debugMeta):pass
Now we can use object-oriented inheritence to inherit the properties of the meta-class into our new class. Every time “ debugMeta” is modified, all of its children that depend on it will inherit the changes. This makes for some interesting syntax where we can automate and add functionality to our class without the need to ever modify it.
# inheriting Base class Calc(Base):  def add(self, x, y):   return x+y
And now using the calculator:
Our calculator class is actually using the debugging behavior that we set up with the metaclass, but note that the data isn’t a child of our object:
Though Python isn’t typically associated with the idea of meta-programming, not only is it perfectly capable of achieving the concept with its unique decorators, it also uses it itself in a lot of ways. I think the biggest advantage that Python has to a lot of other languages where meta-programming is the norm is Python’s inheritance. For what I did above, none of it would have been possible without inheritance, and Python does inheritance really well in my subjective appearance. Since often we associate meta-programming with languages like Lisp or Scheme, it’s very interesting to see meta-programming done in an entirely different — and really cool way in an entirely different language from a completely different paradigm. On top of that, decorators are certainly a cool concept that I really wish more languages would adopt, as it makes the process of meta-programming really simple and straight-forward.
This is especially true when comparing Python to other languages, and more specifically functional languages that I have used for meta-programming. Often tossing around macros and expressions can be powerful, but simultaneously it can be incredibly confusing, and I think Python leverages its syntax very well in the face of that exact problem.