Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python metaclasses vs class decorators

What are the main differences between Python metaclasses and class decorators? Is there something I can do with one but not with the other?

like image 527
gpilotino Avatar asked Nov 22 '09 17:11

gpilotino


People also ask

Why would you use metaclasses in Python?

To create your own metaclass in Python you really just want to subclass type . A metaclass is most commonly used as a class-factory. When you create an object by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass.

How do class decorators overlap with Metaclasses for handling classes in Python?

Anything you can do with a class decorator, you can of course do with a custom metaclass (just apply the functionality of the "decorator function", i.e., the one that takes a class object and modifies it, in the course of the metaclass's __new__ or __init__ that make the class object!-)

What is class decorator in Python?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

What are Metaclasses and when are they used in Python?

A metaclass in Python is a class of a class that defines how a class behaves. A class is itself an instance of a metaclass. A class in Python defines how the instance of the class will behave. In order to understand metaclasses well, one needs to have prior experience working with Python classes.


2 Answers

Decorators are much, much simpler and more limited -- and therefore should be preferred whenever the desired effect can be achieved with either a metaclass or a class decorator.

Anything you can do with a class decorator, you can of course do with a custom metaclass (just apply the functionality of the "decorator function", i.e., the one that takes a class object and modifies it, in the course of the metaclass's __new__ or __init__ that make the class object!-).

There are many things you can do in a custom metaclass but not in a decorator (unless the decorator internally generates and applies a custom metaclass, of course -- but that's cheating;-)... and even then, in Python 3, there are things you can only do with a custom metaclass, not after the fact... but that's a pretty advanced sub-niche of your question, so let me give simpler examples).

For example, suppose you want to make a class object X such that print X (or in Python 3 print(X) of course;-) displays peekaboo!. You cannot possibly do that without a custom metaclass, because the metaclass's override of __str__ is the crucial actor here, i.e., you need a def __str__(cls): return "peekaboo!" in the custom metaclass of class X.

The same applies to all magic methods, i.e., to all kinds of operations as applied to the class object itself (as opposed to, ones applied to its instances, which use magic methods as defined in the class -- operations on the class object itself use magic methods as defined in the metaclass).

like image 113
Alex Martelli Avatar answered Sep 23 '22 10:09

Alex Martelli


As given in the chapter 21 of the book 'fluent python', one difference is related to inheritance. Please see these two scripts. The python version is 3.5. One point is that the use of metaclass affects its children while the decorator affects only the current class.

The script use class-decorator to replace/overwirte the method 'func1'.

def deco4cls(cls):     cls.func1 = lambda self: 2     return cls   @deco4cls class Cls1:     pass   class Cls1_1(Cls1):     def func1(self):         return 3   obj1_1 = Cls1_1() print(obj1_1.func1())  # 3 

The script use metaclass to replace/overwrite the method 'func1'.

class Deco4cls(type):     def __init__(cls, name, bases, attr_dict):         # print(cls, name, bases, attr_dict)         super().__init__(name, bases, attr_dict)         cls.func1 = lambda self: 2   class Cls2(metaclass=Deco4cls):     pass   class Cls2_1(Cls2):     def func1(self):         return 3   obj2_1 = Cls2_1() print(obj2_1.func1())  # 2!! the original Cls2_1.func1 is replaced by metaclass 
like image 39
Hobin C. Avatar answered Sep 21 '22 10:09

Hobin C.