[Tutor] decorators

Christopher King g.nius.ck at gmail.com
Thu Jun 30 19:09:00 CEST 2011


It would be cool if their where decorators that modified decorators. I know
its possible, but I can't think of a use.

On Thu, Jun 23, 2011 at 11:05 PM, Steven D'Aprano <steve at pearwood.info>wrote:

> Robert wrote:
>
>> Is there a good tutorial out there somewhere about decorators? Google
>> doesn't bring up much.
>>
>>
>
> Define "good" :)
>
> I'm interested in what you think about this article:
>
> http://www.artima.com/weblogs/**viewpost.jsp?thread=240808<http://www.artima.com/weblogs/viewpost.jsp?thread=240808>
>
> Personally, I think it's filled with jargon that will be alien to most
> Python coders, but otherwise interesting.
>
> Here's my cheap introduction to decorators...
>
> Before you understand decorators, you have to understand two things about
> Python:
>
> (1) Functions are "first class objects";
>
> (2) and therefore you can write "factory functions".
>
> Everything else is just a bonus.
>
> What I mean by "first class" is best explained by giving an example of the
> opposite, second class objects. In some languages, functions are "special",
> and by special I mean they are more restricted. You cannot (easily, or at
> all) pass a function as an argument to another function, or give it a new
> name. This makes a lot of code hard to write. For instance, you might want
> to create a Grapher application, that lets the user draw the graph of some
> function. The basic algorithm would be something like this:
>
> def grapher(function, low, high, step):
>    for x in range(low, high, step):
>        y = function(x)
>        draw_pixel(x, y)  # draw a pixel on the graph, somehow...
>
> This is easy in Python, but very hard in languages where functions are
> second class. Because they are second class, you cannot pass them as
> arguments:
>
> grapher(math.sin, 0, 100, 1)
>
> is not allowed in some languages, but is allowed in Python.
>
> One consequence of this is the idea of "factory functions" is allowed. You
> can write a function which builds new functions, and returns them:
>
> >>> def factory(x):
> ...     def inner(arg):
> ...         return arg + x
> ...     return inner  # return the function object itself
> ...
> >>> plusone = factory(1)
> >>> plustwo = factory(2)
> >>>
> >>> plusone(23)
> 24
>
>
> Decorators are a special type of factory function. They take as their
> argument a function, and then modify, wrap, or replace the function to
> perform special processing. Because the decorator itself is a function,
> anything you can do in Python, you can do in a decorator. The only
> limitation is that it must take a single argument, expected to be a
> function. (Or a class, in newer versions of Python.) Everything else is up
> to you!
>
> "Decorator syntax" is the special syntax you often see:
>
> @decorator
> def spam():
>    pass
>
>
> is syntactic sugar for the longer version:
>
> def spam():
>    pass
>
> spam = decorator(spam)
>
>
>
> What are decorators good for?
>
> The most common use is to *wrap* the function so as to eliminate
> boilerplate code.
>
> Suppose you have a bunch of functions that look like this:
>
>
> def func(arg):
>    if isinstance(arg, int) and arg > 0:
>        arg = min(arg, 1000)
>        do stuff
>    else:
>        raise ValueError('bad argument')
>
> def func2(arg):
>    if isinstance(arg, int) and arg > 0:
>        arg = min(arg, 1000)
>        do different stuff
>    else:
>        raise ValueError('bad argument')
>
> def func3(arg):
>    if isinstance(arg, int) and arg > 0:
>        arg = min(arg, 1000)
>        do something else
>    else:
>        raise ValueError('bad argument')
>
> All of the functions go through the same boilerplate at the beginning and
> end, testing for a valid argument, raising an error if not, adjusting the
> argument value... Only the "do stuff" parts are different. This is a lot of
> duplicated code. We can reduce the duplication, making it easier to maintain
> and test, by moving all the common code into one place:
>
>
> def test_argument(func):
>    def inner(arg):
>        if not (isinstance(arg, int) and arg > 0):
>            raise ValueError('bad argument')
>        arg = min(arg, 1000)
>        return func(arg)
>    return inner
>
>
> @test_argument
> def func(arg):
>    do stuff
>
> @test_argument
> def func2(arg):
>    do different stuff
>
> @test_argument
> def func3(arg):
>    do something else again
>
>
> The common code (the boilerplate) is now inside the decorator. The
> decorator takes a function as an argument, wraps it with an inner function
> that calls the common boilerplate code, tests the argument, raises an error
> if needed, and returns the result of calling the unique "do stuff" part.
>
>
> --
> Steven
>
> ______________________________**_________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> http://mail.python.org/**mailman/listinfo/tutor<http://mail.python.org/mailman/listinfo/tutor>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20110630/bd8d2186/attachment.html>


More information about the Tutor mailing list