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?
To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function. Here, when we decorate, multiply_together with integer_check, the integer function gets called.
A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
The @classmethod decorator is a built-in function decorator which is an expression that gets evaluated after your function is defined. The result of that evaluation shadows your function definition. A class method receives the class as the implicit first argument, just like an instance method receives the instance.
A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.
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!-)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With