[Python-Dev] Explicit Lexical Scoping (pre-PEP?) (original) (raw)
Phillip J. Eby pje at telecommunity.com
Fri Jul 7 19:25:43 CEST 2006
- Previous message: [Python-Dev] Explicit Lexical Scoping (pre-PEP?)
- Next message: [Python-Dev] doc for new restricted execution design for Python
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
At 09:56 PM 7/6/2006 -0400, Kevin Jacobs <jacobs at bioinformed.com> wrote:
Why not extend the interface to the locals builtin and add a getitem that returns a proxy to access locals defined in other lexical scopes via _{get/set/del}attr:
def counter(num): num = 1 def inc(): locals[1].num += 1 return outer.num return inc
Where, for CPython, locals[n] gives access to NamespaceProxy(sys.getframe(n).flocals).
That doesn't actually work, because sys._getframe(1) will give you inc()'s caller, not the frame where it was defined.
In addition to having a relatively pleasing and explicit syntax, this may be a feasible method for allowing portable introspection into outer scopes without having to export the whole frame object a la sys.getframe(n). I strongly suspect that Jython, IronPython, and PyPy would have little difficulty supporting (and optimizing) this construct.
Lacking core language support, it is easy to roll an object that does just what I suggest. Actual implementation is left to a more motivated reader, of course.
While I hesitate to describe anything as "impossible", I will note that an implementation with the syntax you suggest is not likely to be possible without resorting to ctypes or a C module, because the frame doesn't have a reference to the function, only the code object, and it's the function objects that own the closure cells.
I think the only way you can reasonably accomplish rebinding in Python right now is to have a rebind(func, var=value) function, e.g.:
def counter(num):
def inc():
rebind(inc, num=num+1)
return num
return inc
It doesn't allow augmented assignment, of course, and it also creates a circular reference between 'inc' and itself, requiring garbage collection to clean up.
Anyway, the actual implementation of such a rebind function would basically be a bit of syntax sugar over this cookbook recipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440515
You'd want to just use the keyword argument names to figure out which cells of the function needed changing. And the whole thing would be really slow and complex. You'd probably be better off using function attributes:
def counter(num):
def inc():
inc.num+=1
return inc.num
inc.num = num
return inc
It's not ideal, but this should probably be the pattern we recommend for rebinding, rather than "create a class" or "use an anonymous namespace argument".
It can even be prettied up a little bit with a decorator to set the function attributes.
def counter(num):
@uses(num=num)
def inc():
inc.num+=1
return inc.num
return inc
But that's about as good as it gets, which is nowhere near as good as:
def counter(num):
def inc():
nonlocal num
num+=1
return num
return inc
On the other hand, the rebind() syntax actually is slightly cleaner in some respects:
def counter(num):
def inc():
rebind(num = num+1)
return num
return inc
Maybe we should just make rebind() a fast builtin. ;)
- Previous message: [Python-Dev] Explicit Lexical Scoping (pre-PEP?)
- Next message: [Python-Dev] doc for new restricted execution design for Python
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]