[Python-ideas] 'Injecting' objects as function-local constants
Jan Kaliszewski
zuo at chopin.edu.pl
Thu Jun 16 19:15:25 CEST 2011
Nick Coghlan dixit (2011-06-16, 13:41):
> 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
It is not less 'magic' than what I proposed as variant #3. And, in fact,
it is almost the same -- the only important difference is the place:
imho placing it *before* definition better emphasizes that the binding
is an *early* one.
@inject(mem=collections.Counter(), MAX_MEM=1000)
def do_and_remember(val, verbose=False):
or even (to stress that it is a language syntax construct:
@inject mem=collections.Counter(), MAX_MEM=1000
def do_and_remember(val, verbose=False):
or:
@inject collections.Counter() as mem, 1000 as MAX_MEM
def do_and_remember(val, verbose=False):
or something similar...
Also, such placement is imho more appropriate for @-that-starts-a-line-
-syntax (because it would *resemble* decorating syntax anyway -- and
what's wrong with that?) + we avoid misleading clusters such as:
def do_something(func):
@def mem=colletions.Counter # <- looks a bit like another
@wraps(func) # decorator for wrapper_func()
@my_decorator(mem)
def wrapper_func():
...
Regards.
*j
More information about the Python-ideas
mailing list