Metaprogramming using metaclasses in Python

Metaprogramming using metaclasses in Python

We all have heard a lot about basic concepts classes in Python like their syntax, their attributes, how to create objects, but there are more interesting things about classes in Python.

First of all, in Python everything is an object: we got integers, strings, functions, even the classes are objects. Yes, even them. So we can treat them like any other object, like pass it as a function parameter:

class C: ...

def f(c: type) -> C:
    return c()

obj = f(C)  # instance of the class C        

or return them:

class C: ...

def f() -> type:
    return C

c = f() # class C
obj = c() # instance of class C        

or assign and print them:

class C: ...

c = C

print(c)  # <class '__main__.C'>        

We can also create classes dynamically, using the function type by passing the name of the class, the classes to inherit and the attributes with their values:

def f(self):
    return 2

name = "C"
bases = ()
attrs = {"attr": 1, "f": f}

C = type(name, bases, attrs)  # create the class C using the type function

obj = C()  # instance of class C

print(obj.attr)  # 1
print(obj.f())  # 2        


So, if the classes are objects, how do we call the class of a class? We call it metaclass. A metaclass defines how a class is created. We can use metaclasses to add attributes or functionalities to a class, verify class definitions, track declared classes, and more.

The metaclass type is the default metaclass of every class, and to create a custom metaclass we have to inherit from it:

 class MC(type): ... # metaclass

class C(metaclass=MC): ... # class, its metaclass is MC

obj = C() # object of the class C        


So, what is metaprogramming? Metaprogramming is a technique in which code can read, generate, modify, or transform other code. This includes advanced features like declaring metaclasses. Using metaclasses, we can verify if a class implements a method:

class MC(type):
    def __init__(cls, name, bases, attrs):
        if "f" not in attrs:
            raise TypeError(f"Class {name} must define a 'f' method")
        super().__init__(name, bases, attrs)

# will not raise an error
class C1(metaclass=MC):
    def f(): ...

# will raise an error
class C2(metaclass=MC): ...        

or add a default value to an attribute:

class MC(type):
    def __init__(cls, name, bases, attrs):
        if "attr" not in attrs:
            cls.attr = "default"
        super().__init__(name, bases, attrs)

class C(metaclass=MC): ...

print(C.attr)  # default        

or write beautiful singleton solutions:

class Singleton(type):
    instance = None

    def __call__(cls, *args, **kwargs):
        if not cls.instance:
            cls.instance = super().__call__(*args, **kwargs)
        return cls.instance

class C1(metaclass=Singleton): ...        


In conclusion, metaclasses are a powerful feature in Python, enabling advanced customization and control over class behavior. While they can be complex, their ability to enforce rules, add attributes and patterns like singletons can lead to more maintainable and elegant code in the right scenarios.

要查看或添加评论,请登录

Danilo Nu?ez的更多文章

  • Concurrency in Python

    Concurrency in Python

    Concurrency creates the illusion of parallelism by switching between tasks while parallelism is doing many tasks at the…

    2 条评论

社区洞察

其他会员也浏览了