[IPython-dev] Musings: syntax for high-level expression of parallel (and other) execution control

Fernando Perez fperez.net@gmail....
Sun Sep 6 01:36:50 CDT 2009


On Sat, Sep 5, 2009 at 2:52 PM, Edward K. Ream <edreamleo@gmail.com> wrote:
> On Fri, Sep 4, 2009 at 3:53 PM, Edward K. Ream <edreamleo@gmail.com> wrote:
>>
>> Also, it is in no way an abuse of decorators to use them in unexpected,
>> unusual, creative ways, provided only that you are not relying on some
>> undocumented accidental feature.
>
> Inspired by this thread, I decided to deepen my understanding of
> decorators.  To state my conclusion first, to truly understand decorators it
> is a good idea to completely ignore pep 318 and all related tutorials :-)

Almost all :)  I think Matthew Brett's (disclaimer: a good friend and
colleague) is actually quite nice and to the point:

https://cirl.berkeley.edu/mb312/data_docs/decorating_for_dummies.html

though it does have the same misconception that just about every other
document about decorators has, namely

"""a function, that takes a function as input, and ***returns a function*** """

The part between ** above is not correct,  and this is a subtle but
critical point here.  As you correctly cite in the ref guide:

> Decorator expressions are evaluated when the function is defined, in the
> scope that contains the function definition. The result must be a callable,

The result of the *decorator expression*, that is, *the line that
starts with '@'*, must be a callable.  But the result of evaluating
*that* on the function afterwards need not be a callable at all, as we
can easily see:

In [18]: def funnydeco(func):
   ....:     return 'Hi, I am a decorator...'
   ....:

In [19]: @funnydeco
   ....: def f(x):
   ....:     return x+1
   ....:

In [20]: f(10)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/fperez/research/code/contexts/simple.py in <module>()
[...]
TypeError: 'str' object is not callable

In [21]: f
Out[21]: 'Hi, I am a decorator...'

This means that our 'inline decorators' can not be chained, since they
are 'greedy' in that they consume the function they are meant to be
applied to.  They can return the value of the called function though,
which can be very useful as seen here:

def execute(func):
    return func()

def simple2(n):

    @execute
    def s():
        c = 0.0
        for i in range(n):
            c += i**2
        return c

    return s

By returning a value in the block and later using the name of the
block, we can feed back locals to the surrounding scope.  This is the
hack that 'nonlocal' in 3.x makes obsolete, but for now we'll have to
make do with mutables or this trick.

> Imo, this is a rare example where the most consise explanation is also the
> clearest and best.  It is best because it does not deal with the blah blah
> blah of expectations.  It implicitly says that one is free to use decorators
> in *any* way, subject only to the constraint that the decorator expression
> evaluates to a callable.  Failure of the decorator to evaluate to a callable
> of *some* kind is the only way to "abuse" a decorator, and the compiler will
> not allow such abuse :)  In particular, there is no requirement that the
> callable be in *any* way related to func!

Yes, and that's what we're taking advantage of here.  We'll see what
good uses we can find as we work with the idea.

Cheers,

f


More information about the IPython-dev mailing list