On Wed, Mar 20, 2019 at 7:23 AM <adam.pre...@gmail.com> wrote: > > I got hit on the head and decided to try to write something of a Python > interpreter for embedding. I'm starting to get into the ugly stuff like > scoping. This has been a good way to learn the real deep details of the > language. Here's a situation: > > >>> meow = 10 > >>> def vartest(): > ... x = 1 > ... y = 2 > ... def first(): > ... x = 3 > ... meow = 11 > ... return x > ... def second(): > ... y = 4 > ... meow = 12 > ... return y > ... return first() + second() + x + y + meow > ... > >>> vartest() > 20 > > first() and second() are messing with their own versions of x and y. Side > note: the peephole optimizer doesn't just slap a 3 and 4 on the stack, > respectively, and just return that. It'll actually do a STORE_NAME to 0 for > each. The meow variable is more peculiar. The first() and second() inner > functions are working on their own copy. However, vartest() is using the one > from the calling scope, which is the REPL. > > I can see in vartest() that it's using a LOAD_GLOBAL for that, yet first() > and second() don't go searching upstairs for a meow variable. What is the > basis behind this? >
Both first() and second() assign to the name "meow", so the name is considered local to each of them. In vartest(), the name isn't assigned, so it looks for an outer scope. (Side note: the peephole optimizer COULD be written to simplify that case, and there's no reason it can't in the future. Probably wouldn't save all that much work though.) > I tripped on this in my unit tests when I was trying to note that a class' > constructor had run successfully without getting into poking the class' > variables. I was trying to have it set a variable in the parent scope that > I'd just query after the test. > > It looks like in practice, that should totally not work at all: > >>> meow = 10 > >>> class Foo: > ... def __init__(self): > ... meow = 11 > ... > >>> f = Foo() > >>> meow > 10 > > ...and it's on me to carve out a throwaway inner meow variable. But hey, > let's kick meow up to the class level: You've made "meow" a local variable here. If you want it to be an attribute of the object, you would need to apply it to the object itself ("self.meow = 11"). > >>> meow = 10 > >>> class Foo: > ... meow = 11 > ... def __init__(self): > ... pass > ... > >>> f = Foo() > >>> meow > 10 This has created a class attribute. If you inspect "Foo.meow" (or "f.meow", because of the way attribute lookup works), you'll see the 11. > So I guess it's a different ballgame for classes entirely. What are the rules > in play here for: > 1. A first-level function knowing to use a variable globally > 2. A Second-level function using the name locally and uniquely > 3. Classes having no visibility to upper-level variables at all by default In all cases, you can use the declaration "global meow" to affect the global (module-level) name. In the inner-function case, you can also use "nonlocal x" to affect the "x" in the outer function. This applies any time you *assign to* a name; if you just reference it, the lookup will extend outwards in the most expected way. ChrisA -- https://mail.python.org/mailman/listinfo/python-list