I’m revisiting variable scope technicalities in Python for my personal interpreter project. Some time ago, I asked about that and got the tip that CPython has a multi-pass system that figures out variables, scopes, and bindings ahead of generating byte code. I started doing similar, and I was doing great with just functions, but I realized I wasn’t managing classes with it correctly.
It’s pretty natural to delve into some really obscure stuff when you start testing for this stuff, and I can say that I’ve lost myself.
So here’s something that kind of puzzles me:
a = 1 class Foo1: a += 1 def __init__(self): global a self.local_a = a + 1 class Foo2: a += 100 def __init__(self): global a self.local_a = a + 1 f = Foo1() print(a) print(Foo1.a) print(Foo1.Foo2.a) g = Foo1.Foo2() print(f.local_a) print(g.local_a) print(a)
1 2 101 2 2 1
It looks like Foo1 and Foo2 on first invocation will get a copy of the global a and do their own thing with their copy. The initializers grab the root level one.
If I qualify the class members with ‘global a’ then I’ll be playing with the global one at the root just fine. Otherwise, nothing happens to it. If I want Foo2 to touch the global ‘a’, I have to make it global in Foo1 as well:
class Foo1: global a # Need this if I want Foo2 to see it. ... class Foo2: global a
If I had nested functions, I wouldn’t have to “carry” the global.
I hope somebody can explain how classes muddle with variable scopes. At first glance, it looked like a class declaration creates a jail blocking against upper scope and takes copies of globals inside of itself. Then a class method runs and can break right out of that to reach globals outside of the class anyways.
…work in a language for nearly 15 years and then break your brain on this kind of thing…