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 <st...@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@python.org > To unsubscribe or change subscription options: > http://mail.python.org/**mailman/listinfo/tutor<http://mail.python.org/mailman/listinfo/tutor> >
_______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor