Class-based decorators use call and hold state; function-based use closures
A function-based decorator is a function that returns a closure over the wrapped function. A class-based decorator is a class whose instances are callable: init receives the function (or the parameters, in factory form), and call implements the behavior. The class form is preferable when the decorator has meaningful state or behavior of its own, for example a cache with a cache_clear() method, multiple helper methods, or inheritance. The function form is simpler and usually the right default. The subtle trap is that a class-based decorator instance is not a function, so it is not a descriptor by default and will not bind self when applied to an instance method unless you implement get.
Use function-based for stateless or simple stateful wrapping; use class-based when you need extra public methods, inheritance, or richer state.
Always call functools.update_wrapper(self, fn) in init for class-based decorators; functools.wraps is the function equivalent.
Implement get on the class-based decorator to return a bound method when accessed through an instance, otherwise method binding breaks.
Trade-off: class instances carry a per-decorated-function object and add an attribute lookup per call; closures are lighter but harder to extend.
Common mistake: expecting a class-level counter to be shared across all decorated functions when the class is applied directly. Each application creates a new instance, so state is per-function.
Version note: functools.update_wrapper and wrapped behavior is consistent across Python 3; the descriptor protocol used by get has been stable since Python 2.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience