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)
Result:
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…