Thank you Anand for bringing up this thought provoking question! Regardless of the possible explanations, it has to be said that this behaviour is indeed confusing. This will conflict with the general idea of LEGB scoping order (i.e. Local, Enclosing Function local, Global, Built-in). The functions f and g apparently show conflicting behavior in the way variables x and y are treated respectively.
To summarize the confusions: 1. in *f*, print(x) statement under class definition causes an exception. Shouldn't it have accepted the *x* that was declared in its Enclosing function? 2. If we accept that as the default behaviour, why on earth is the *gety * function inside *g* returning value 1 instead of 2? (It would have returned 1 regardless of its class had a statement like 'y=1'!) The following might explain this: The original variable '*y*' in function *g* and the '*y*' inside *gety*function are the same object! But the ' *y*' in the class Foo local is different. You can check it this way: def g(): y = 1 *print 'id(y) originally in g:', id(y)* class Foo(): y = 2 *print 'id(y) in class Foo:', id(y)* def gety(self): *print 'id(y) in function gety inside Foo:', id(y)* return y foo = Foo() print (y, foo, foo.gety()) But why?? * * *The x in function f and y in function g, though may look identical to us, are stored differently by python! *This can be seen from the disassembly of both functions: >>> dis(f) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (x) 3 6 LOAD_CONST 2 ('Foo') 9 LOAD_CONST 4 (()) 12 LOAD_CONST 3 (<code object Foo at 0x101102bb0, file "class_check.py", line 3>) 15 MAKE_FUNCTION 0 18 CALL_FUNCTION 0 21 BUILD_CLASS 22 STORE_FAST 1 (Foo) 7 25 LOAD_FAST 0 (x) 28 LOAD_FAST 1 (Foo) 31 LOAD_ATTR 0 (x) 34 BUILD_TUPLE 2 37 PRINT_ITEM 38 PRINT_NEWLINE 39 LOAD_CONST 0 (None) 42 RETURN_VALUE ======================================================== >>> dis(g) 10 0 LOAD_CONST 1 (1) 3 STORE_DEREF 0 (y) 11 6 LOAD_CONST 2 ('Foo') 9 LOAD_CONST 4 (()) 12 LOAD_CLOSURE 0 (y) 15 BUILD_TUPLE 1 18 LOAD_CONST 3 (<code object Foo at 0x101102d30, file "class_check.py", line 11>) 21 MAKE_CLOSURE 0 24 CALL_FUNCTION 0 27 BUILD_CLASS 28 STORE_FAST 0 (Foo) 15 31 LOAD_FAST 0 (Foo) 34 CALL_FUNCTION 0 37 STORE_FAST 1 (foo) 16 40 LOAD_DEREF 0 (y) 43 LOAD_FAST 1 (foo) 46 LOAD_ATTR 0 (y) 49 LOAD_FAST 1 (foo) 52 LOAD_ATTR 1 (gety) 55 CALL_FUNCTION 0 58 BUILD_TUPLE 3 61 PRINT_ITEM 62 PRINT_NEWLINE 63 LOAD_CONST 0 (None) 66 RETURN_VALUE It can be noticed that x is stored as STORE_FAST while y is stored as STORE_DEREF. The excellent 'Python Innards series' (which I can understand only very little) explains this in one of the articles http://tech.blog.aknin.name/2010/06/05/pythons-innards-naming/ : "The secret sauce here is that at compilation time, if a variable is seen to be resolved from a lexically nested function, it will not be stored and will not be accessed using the regular naming opcodes. Instead, a special object called a cell<http://docs.python.org/py3k/c-api/cell.html#cell-objects>is created to store the value of the object. When various code objects (the outer function, the inner function, etc) will access this variable, the use of the *_DEREF opcodes will cause the cell to be accessed rather than the namespace of the accessing code object." additional reference: Python Closure: Link1<http://ynniv.com/blog/2007/08/closures-in-python.html> Link2 <http://www.shutupandship.com/2012/01/python-closures-explained.html>, Regards, Abdul Muneer -- Follow me on Twitter: @abdulmuneer <http://twitter.com/#%21/abdulmuneer> On Tue, Dec 4, 2012 at 4:26 PM, steve <st...@lonetwin.net> wrote: > On Tuesday 04 December 2012 09:24 AM, Anand Chitipothu wrote: > >> Python scoping rules when it comes to classes are so confusing. >> >> Can you guess what would be output of the following program? >> >> x = 1 >> >> class Foo: >> print(x) >> > > Prints the global x > > > x = x + 1 >> print(x) >> > Prints the local x, with the reference to the global x lost in the classes > scope. > > >> print(x, Foo.x) >> > > prints (1, 2) -- ie: the 'global x' and the class local x. So, does the > right thing. What were you expecting ? > > > >> Now take the same piece of code and put it in a function. >> >> def f(): >> x = 1 >> >> class Foo: >> print(x) >> x = x + 1 >> print(x) >> >> print(x) >> print(Foo.x) >> >> f() >> >> > Again, global versus local difference for the /class/. Still not sure what > you were expecting, > > > To add more to your confusion, try this too: >> >> def g(): >> y = 1 >> class Foo: >> y = 2 >> def gety(self): >> return y >> >> foo = Foo() >> print(y, foo.y, foo.gety()) >> >> g() >> >> Ok, this is slightly confusing but still consistent. You'd understand > the source of your confusion if you changed the definition for gety() to: > ... > ... > def gety(self): > return self.y > ... > ... > > > > Does it make any sense? >> > Well, it does if you know the rules. > > http://effbot.org/pyfaq/what-**are-the-rules-for-local-and-** > global-variables-in-python.htm<http://effbot.org/pyfaq/what-are-the-rules-for-local-and-global-variables-in-python.htm> > > Try this: > > > x = 1 > class Foo: > print(x) # this is the global x > x = x + 1 # this is the local x > print(x) > global x # now lets be explicit > print(x, Foo.x) > > what happened here ? > > cheers, > - steve > > > ______________________________**_________________ > BangPypers mailing list > BangPypers@python.org > http://mail.python.org/**mailman/listinfo/bangpypers<http://mail.python.org/mailman/listinfo/bangpypers> > _______________________________________________ BangPypers mailing list BangPypers@python.org http://mail.python.org/mailman/listinfo/bangpypers