[Python-ideas] Moving return above statements [was Re: Syntax for defining parametric decorators]

Steven D'Aprano steve at pearwood.info
Thu Jul 12 03:37:00 CEST 2012


Nick Coghlan wrote:
> On Mon, Jul 9, 2012 at 7:47 PM, Stefan Behnel <stefan_ml at behnel.de> wrote:
>> >From an innocent look, I have no idea what the syntax is supposed to mean.
>> Clearly doesn't hint at a factory for me.
> 
> I should also mention that I have a different proposal that affects
> the way one would write functions-that-returns-functions. I've been
> messing around with the idea of statement local namespaces for years
> (see PEP 3150) trying to find something that I consider better than
> the status quo, and PEP 403's statement local function and class
> definitions (http://www.python.org/dev/peps/pep-0403/) are the current
> incarnation.
> 
> With those, the number of statements in a simple wrapping decorator
> factory doesn't change, but the return statements can be moved above
> their respective function definitions:
> 
>     def notify_on_call(callback, *cb_args, **cb_kwds):
>         in return decorator
>         def decorator(f):
>             in return wrapped
>             @functools.wraps(f)
>             def wrapped(*args, **kwds):
>                 callback(*cb_args, cb_kwds)
>                 return f(*args, **kwds)


I *really* don't like the way that the "in return" statement reads like it 
creates a new block, but the following lines are not indented. If I were 
writing this as pseudo-code for a human reader, I would surely indent the 
following lines.

Even if the part following the "in return" were limited to a single 
expression, I would prefer to indent it if it appears on another line. Using 
lambda as an example:


# Best
lambda x: expression

# Acceptable
lambda x:\
     expression


# Unacceptable
lambda x:\
expression


Looking back at "in return", here's a simple example which doesn't use nested 
functions. Compare:


def function(arg):
     in return value
     value = process(arg)
     print("Value is", value)

versus:

def function(arg, verbose=False):
     in return value:
         value = process(arg)
         print("Value is", value)


The lack of indentation (and trailing colon) makes the first extremely 
unpythonic -- everything else that creates a new block is indented. 
Consequently I hate the first one and am merely cold to the second.



> Rather than the current out-of-order:
> 
>     def notify_on_call(callback, *cb_args, **cb_kwds):
>         def decorator(f):
>             @functools.wraps(f)
>             def wrapped(*args, **kwds):
>                 callback(*cb_args, cb_kwds)
>                 return f(*args, **kwds)
>             return wrapped
>         return decorator


I would not describe that as "out-of-order". Seems to me that it is precisely 
in order: first you create the object (a function), then you return it. You 
can't return something before it exists.

It seems to me that this proposal, and the older PEP 3150, are the ones which 
are out-of-order: you use things before they are defined.

For what it's worth, I have slightly warmed to PEP 3150 and would give it a 
very tentative +0.125:

def notify_on_call(callback, *cb_args, **cb_kwds):
     return decorator given:
         def decorator(f):
             @functools.wraps(f)
             def wrapped(*args, **kwds):
                 callback(*cb_args, cb_kwds)
                 return f(*args, **kwds)
             return wrapped


I don't think the above is any improvement at all on the status quo, but 3150 
would allow you to write maths expressions more mathematically:


def func(x):
     return a**2 + 3*a given:
         a = 1/(sin(x*pi)) + (cos(x)-1)/2


which I'm not sure will help people used to reading code, but it should at 
least be familiar territory to mathematicians and maths geeks.


(Aside: I actually prefer that bikeshed to be called "where" rather than "given".)



-- 
Steven



More information about the Python-ideas mailing list