Reputation: 190769
Compared to decorators applied to a function, it's not easy to understand the decorators applied to a class.
@foo
class Bar(object):
def __init__(self, x):
self.x = x
def spam(self):
statements
What's the use case of decorators to a class? How to use it?
Upvotes: 7
Views: 917
Reputation: 229341
One use case I can think of is if you want to wrap all the methods of a class with one function decorator. Say you have the following decorator:
def logit(f):
def res(*args, **kwargs):
print "Calling %s" % f.__name__
return f(*args, **kwargs)
return res
And the following class:
>>> class Pointless:
def foo(self): print 'foo'
def bar(self): print 'bar'
def baz(self): print 'baz'
>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
foo
bar
baz
You can decorate all the methods:
>>> class Pointless:
@logit
def foo(self): print 'foo'
@logit
def bar(self): print 'bar'
@logit
def baz(self): print 'baz'
>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
Calling foo
foo
Calling bar
bar
Calling baz
baz
But that is LAME! Instead you can do this:
>>> def logall(cls):
for a in dir(cls):
if callable(getattr(cls, a)):
setattr(cls, a, logit(getattr(cls, a)))
return cls
>>> @logall
class Pointless:
def foo(self): print 'foo'
def bar(self): print 'bar'
def baz(self): print 'baz'
>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
Calling foo
foo
Calling bar
bar
Calling baz
baz
UPDATE: A more generic version of logall
:
>>> def wrapall(method):
def dec(cls):
for a in dir(cls):
if callable(getattr(cls, a)):
setattr(cls, a, method(getattr(cls, a)))
return cls
return dec
>>> @wrapall(logit)
class Pointless:
def foo(self): print 'foo'
def bar(self): print 'bar'
def baz(self): print 'baz'
>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
Calling foo
foo
Calling bar
bar
Calling baz
baz
>>>
Full disclosure: I've never had to do this and I just made this example up.
Upvotes: 6
Reputation: 881595
It replaces the vast majority of classic good uses for custom metaclasses in a much simpler way.
Think about it this way: nothing that's directly in the class body can refer to the class object, because the class object doesn't exist until well after the body's done running (it's the metaclass's job to create the class object -- usually type
's, for all classes without a custom metaclass).
But, the code in the class decorator runs after the class object is created (indeed, with the class object as an argument!) and so can perfectly well refer to that class object (and usually needs to do so).
For example, consider:
def enum(cls):
names = getattr(cls, 'names', None)
if names is None:
raise TypeError('%r must have a class field `names` to be an `enum`!',
cls.__name__)
for i, n in enumerate(names):
setattr(cls, n, i)
return cls
@enum
class Color(object):
names = 'red green blue'.split()
and now you can refer to Color.red
, Color.green
, &c, rather than to 0
, 1
, etc. (Of course you normally would add even more functionality to make "an enum
", but here I'm just showing the simple way to put such functionality addition in a class decorator, rather than needing a custom metaclass!-)
Upvotes: 23