[Python-ideas] 'Injecting' objects as function-local constants

Nick Coghlan ncoghlan at gmail.com
Thu Jun 16 05:41:17 CEST 2011

On Thu, Jun 16, 2011 at 9:15 AM, Jan Kaliszewski <zuo at chopin.edu.pl> wrote:
> One question is whether it is technically possible to avoid introducing
> a new keyword (e.g. staticlocal) explicitly marking injected locals.
> Using such a keyword would be redundant from user point of view and
> non-DRY:

There has to be *something* that tells the compiler to generate
different bytecode (either a local lookup, a closure lookup or
something new). This lands in the same category as "nonlocal" and
"global" (and no, a "magic" decorator that the compiler recognises is
not a reasonable alternative).

That's why I quite liked the @def idea - it would just define a
sequence of simple statements that the compiler will run at function
*definition* time that is then used to preseed the local namespace at
function *call* time (remember, it is intended to be a mnemonic for
"at definition time" and the use of '@' also reflects the fact that
this code would run just before function decorators are executed).
Just like a class body, the @def code itself would be thrown away and
only the resulting preseeded locals information would be retained. To
allow rebinding to work correctly, this shared state could be
implemented via cell variables rather than ordinary locals.

Possible implementation sketch:

  Compile time:
    - @def statements are compiled in the context of the containing
scope and stored on a new ASDL sequence attribute in the Function AST
    - symtable analysis notes explicitly which names are bound in the
@def statements and this information is stored on the code object
    - code generation produces a cell lookup for any names bound in
@def statements (even if they are also assigned as ordinary locals)
    - Raises a SyntaxError if there is a conflict between parameter
names and names bound in @def statements

  Definition time:
    - @def statements are executed as a suite in the context of the
containing scope but using a *copy* of the locals (so the containing
scope is not modified)
    - names bound in the @def statements (as noted on the code object)
are linked up to the appropriate cells on the function object

  Execution time:
    - Nothing special. The code is executed and references the cell
variables precisely as if they came from a closure.

An API could be provided in functools to provide a clean way to view
(and perhaps modify) the contents of the cells from outside the
function. And yes, I'm aware this blurs the line even further between
functions and classes, but the core difference between "a specific
algorithm with some persistent state" and "persistent state with
optional associated algorithms" remains intact.

And, to repeat the example of how it would look in practice:

   def do_and_remember(val, verbose=False):
         @def mem=collections.Counter()
         # Algorithm that calculates result given val
         mem[val] += 1
         if verbose:
             print('Done {} times for {!r}'.format(_mem[val], val))
         return result


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-ideas mailing list