What are the main differences between Python metaclasses and class decorators? Is there something I can do with one but not with the other?
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).

- 854,459
- 170
- 1,222
- 1,395
-
9Well.. it's been 4 years and I found this question at google. I just want to make a note about your example. I've defined a decorator `foo(cls)` which has 2 statements: `cls.__str__ = lambda self: "peekaboo!"` and right after that `return cls`, and it actually worked - I've set the `__str__` method of some class with the decorator rather than with a complicated metaclass.. I just wanted to note this, and maybe get some comment to know if I'm missing something. – rboy Mar 26 '14 at 18:46
-
@AlexMartelli I'm also interested in what rboy has noted. I still can't find a good clear definition of what is the difference in functionality of metaclass and class decorator. – antitoxic Aug 22 '15 at 22:32
-
16@antitoxic, what rboy has done affects `print(X())`, the printing of *instances* of class `X`, *not* what I very explicitly addressed in my answer, `print(X)`, the printing of *the class itself*. Re-read the last paragraph of my answer. – Alex Martelli Aug 28 '15 at 21:41
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

- 671
- 1
- 8
- 17
-
would you please explain why is the given code behaving differently ? – Marine Galantin Jul 02 '20 at 16:23
-
Marine, so from what I gather with this is when the Cls2 which has the meta class of Deco4cls, is being inherited by Cls2_1 in the __init__ call in the Deco4cls there is a lambda expression that sets the method of the inheriting class to return 2. (cls.func1 = lambda self : 2) . With the use of super it will change, if Cls2_1 calls super func1 should return 3 still, not tested just speculation. – Aaron B Mar 15 '21 at 19:00
-
Regarding the metaclass example. I think you could do the same with a decorator? You'd have to modify the `__init__` method in the decorator. In other words: Two two examples behave differently because they do different things. – Gerrit Begher Jan 06 '22 at 07:52
-
1@GerritBegher this would not work, as the inheriting class (Cls2_1) will still override func1(self) in its class body. The metaclass example *does not* modify the __init__() method of the class itself, it rather modifies the instance of the metaclass (i.e. the class, and the inheriting class) *after* their creation. This means the function gets overriden even outside of instance creation (obj2_1) of the class. To do the same thing with a decorator, you'd have to apply the decorator to Cls2_1, rather than to Cls2 – Aprillomat Dec 06 '22 at 20:26