[Tutor] decorators (the "at" sign)?

Lie Ryan lie.1296 at gmail.com
Tue Oct 26 14:30:32 CEST 2010


On 10/26/10 13:46, Alex Hall wrote:
> Hi all,
> Now that I am able to run the source code of an open source
> application I hope to one day help develop, I am trying to understand
> how it works. One thing I keep seeing is an at sign followed by a
> word, usually (maybe always) immediately preceeding a function
> definition. For example, and I know this exact code will not make much
> sense, but it gives the idea:
> class Bing(Messages, Updating, Dismissable):
> 
>  @set_index
>  def get_url(self, index=None):
>   return self.storage[index]['Url']
> 
> What is the "@set_index" for? Specifically, what is the at sign doing?
> Google was only able to provide me with a very vague idea of what is
> going on, though it seems to crop up a lot in classmethod and
> staticmethod calls (not sure about those either). I read PEP 318, but
> it was not much help since I am coming at this having no idea what I
> am looking at. The PEP did explain why I have never run into this
> before, though - it is apparently specific to Python. I see this sort
> of thing all over this source code so it seems like a good idea to get
> exactly what it is for. TIA!


The decorator syntax is really just a shorthand, from this:

@decorator
def func(arg):
    pass

is equivalent to:

def func(arg):
    pass
func = decorator(func)


basically, a decorator is a function that takes an function as a
parameter/callable and (usually) returns another function/callable as
return value.


A slightly advanced usage of decorator:

def trace(func):
    """ trace will print the func's name, the number of times
        func have been called, the arguments it's called with,
        and the return value of func whenever func is called.
    """

    # define an inner function
    def _decorator(arg):
        print ("The %sth call of %s with arg: %s" %
            (_decorator._numcall, func.__name__, arg))

        _decorator._numcall += 1
        ret = func(arg)

        print "finished", func.__name__, "returns:", ret

        return ret

    # this is used to store the number of times
    # the decorated function is called
    _decorator._numcall = 0

    # disable the decorator when debugging is enabled
    if __debug__: # or: return _decorator if __debug__ else func
        return _decorator
    else:
        return func

@trace
def twice(arg):
    return 2 * arg
# the @trace makes it as if you do this:
# twice = trace(twice)



$ # let's start the program
$ python decor.py
The 0th call of twice() with arg: 30
finished twice() returns: 60
The 1th call of twice() with arg: 3
finished twice() returns: 6
The 2th call of twice() with arg: 4
finished twice() returns: 8
74
$
$ # now call it with debugging disabled:
$ python -O decor.py
74


another nifty use of decorator is for event handling for a hypothetical
GUI toolkit (I've yet to actually see a toolkit that uses this syntax yet):

@button.on_click
def shoot(button):
    ...
@window.on_keypress('p')
def pause(key):
    ...


other built-in functions designed for decorator syntax is @property,
@classmethod, and @instancemethod. Decorator can become extremely
powerful when combined with the descriptor protocol (.__get__, .__set__).



More information about the Tutor mailing list