[Python-ideas] Tweaking closures and lexical scoping to include the function being defined

Ron Adam ron3200 at gmail.com
Wed Sep 28 07:48:31 CEST 2011


On Tue, 2011-09-27 at 06:08 -0400, Nick Coghlan wrote:
> On Tue, Sep 27, 2011 at 12:12 AM, Guido van Rossum <guido at python.org>
> wrote:
> > Somehow you triggered a thought in my head: maybe se should stop
> > focusing on the syntax for a while and focus on the code we want to
> be
> > generated for it. If we can design the bytecode, perhaps that would
> > help us come up with the right keyword.
> 
> Heh, doing exactly that (i.e. thinking in terms of how it would *work*
> and how I would *explain* those semantics to someone in terms of
> existing language concepts) is where my nonlocal suggestion came from
> in the first place :)
> 
> As I see it, there are 3 possible ways to approach this feature, and
> they vary mainly in terms of when the initialisation expressions get
> evaluated:
> 1. Prior to function definition (i.e. the same as default argument
> values)
> 2. After function definition but before decorators are applied
> 3. After decorator application 


I keep going back to the point I was when I wanted to unpack **kwds into
locals(). But that is only part of the problem.  The other half is
making the function use the decorator as it's closure, or to supply a
new one.


> For the recursion use cases, I'd suggest actually offering a
> 'functools.rebind_closure' function that provided an officially
> supported way to modify a closure reference in a function. You could
> then write a function with an early binding reference to itself as
> follows:

+1, I think this is the right approach. But it needs to accept a
dictionary because we can't unpack an argument list into locals, and we
don't want to specialize (rewrite) the decorator for each individual
case!

We want to be able to write a *generalized* decorator.



>     @example
>     def global_accumulator(x):
>         nonlocal tally from 0 # Insert preferred spelling here
>         tally += x
>         return tally


# A generalized decorator for adding closure values.

def add_closure(f):
    """ A decorator to add explicit closure values. """
    def _(**kwds):
        re_define(f, closure=kwds)  #As if it was defined here!
        return f
    return _

@add_closure(tally=0):
def global_accumulator(x):
    nonlocal tally              #Nothing new here. ;-)
    tally += x
    return tally


A re_define function could be extended to make other adjustments as
well.  For example a 'defaults=" keyword, that add to locals instead of
closures.  Those would be set back to their default value on each call.

It may be better for a rebind or redefine to return a new function.

If we add a way to update locals from kwds, Then the rebind or redefine
function can be simpler.

 def add_closure(f):
     """ A decorator to add explicit closure values. """
     def _(**kwds):
         replace_locals(kwds)
         return redefine(f)          # as if it was defined here.
     return _



# Recursive example that works even if the original function is renamed.

def set_this(f):
    """ A decorator that gives a function a visible name "this". """
    f.rebind_closure(this=f)

@set_this
def factorial(x):
    if x = 0:
        return 1
    return x * this(x-1)

Nonlocal isn't needed as we don't write to "this".

The rebind_closure function would be able to take '**kwds' as it's
argument, so it could be used in the accumulator example as well.

No changes to the nonlocal statement are needed if we can rebind, or
re_define functions.  These complement nonlocal and allow doing things
that are not possible (or easy) at this time.  Mainly a way to write
nice *GENERALIZED* decorators to address the issues we've been
discussing. 

Cheers,
   Ron


    




More information about the Python-ideas mailing list