Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorators applied to class definition with Python

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?

like image 795
prosseek Avatar asked Oct 05 '10 02:10

prosseek


People also ask

How do you implement decorators on a class in Python?

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.

What is decorator in Python definition?

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.

What is class method decorator in Python?

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.

How are decorators used in Python?

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.


2 Answers

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!-)

like image 155
Alex Martelli Avatar answered Sep 19 '22 07:09

Alex Martelli


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.

like image 44
Claudiu Avatar answered Sep 22 '22 07:09

Claudiu