Reputation: 212
I have a class defined inside a python method as follows
c = None
def foo():
i = 1
class bar( object ):
def show( self ):
print i
global c
c = bar()
foo()
print c.show() #prints 1
The above code prints 1 which is thee value of i
.
Where is the 'i' stored for the instance of bar to access ? Instances created outside the class using c.__class__()
can also access i
Upvotes: 0
Views: 97
Reputation: 309929
You're accessing the variable via a closure. In python2.x, i
's value is actually stored on the show
function object in the func_closure
1 attribute:
>>> c = None
>>> def foo():
... i = 1
... class bar( object ):
... def show( self ):
... print i
... global c
... c = bar()
...
>>> foo()
>>> c.show.im_func.func_closure[0].cell_contents
1
We can gain a little more insight by looking at the disassembed bytecode:
>>> dis.dis(c.show.im_func)
5 0 LOAD_DEREF 0 (i)
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
Ahh, so it looks like objects that are accessed via closures are loaded by the LOAD_DEREF
OP code which also tells python which cell to look in (in this case the 0th).
1__closure__
and __func__
are the python3.x names for func_closure
and im_func
, respectively. They're aliased in python2.6 to aid with writing forward compatible code.
Upvotes: 3
Reputation: 1122282
You are accessing a closure; i
is closed over by the nested scope of bar.show()
. The same would happen to variables used by nested functions without a class.
You can see the closure by introspecting the method; I've altered your code a little to just return the class as it simplifies things:
>>> def foo():
... i = 1
... class bar( object ):
... def show( self ):
... print i
... return bar
...
>>> c = foo()()
>>> c
<__main__.bar object at 0x104c7ac90>
>>> c.show
<bound method bar.show of <__main__.bar object at 0x104c7ac90>>
>>> c.show.__func__
<function show at 0x10535c758>
>>> c.show.__func__.__closure__
(<cell at 0x1132a0440: int object at 0x100502818>,)
>>> c.show.__func__.__closure__[0].cell_contents
1
So c.show
is a bound method, c.show.__func__
the original show
function, and c.show.__func__.__closure__
is the tuple of closure cells. I've accessed the current value of the cell with the closure.cell_contents
attribute.
The parent function foo
, is also aware that the variable i
must be kept around, Python recorded that information in the code object for the function:
>>> foo.__code__.co_cellvars
('i',)
and the bytecode for the function creates a closure cell as part of the code:
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (1)
3 STORE_DEREF 0 (i)
3 6 LOAD_CONST 2 ('bar')
9 LOAD_GLOBAL 0 (object)
12 BUILD_TUPLE 1
15 LOAD_CLOSURE 0 (i)
18 BUILD_TUPLE 1
21 LOAD_CONST 3 (<code object bar at 0x1132a4bb0, file "<stdin>", line 3>)
24 MAKE_CLOSURE 0
27 CALL_FUNCTION 0
30 BUILD_CLASS
31 STORE_FAST 0 (bar)
6 34 LOAD_FAST 0 (bar)
37 RETURN_VALUE
The LOAD_CLOSURE
and MAKE_CLOSURE
byte codes work together to produce closure cells and passing it on to the code object that will produce the class (handled by the LOAD_CONST
bytecode at index 21); the class bytecode passes that closure on again to the show
function:
>>> dis.dis(foo.__code__.co_consts[3])
3 0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
4 6 LOAD_CLOSURE 0 (i)
9 BUILD_TUPLE 1
12 LOAD_CONST 0 (<code object show at 0x1132a4330, file "<stdin>", line 4>)
15 MAKE_CLOSURE 0
18 STORE_NAME 2 (show)
21 LOAD_LOCALS
22 RETURN_VALUE
Inside bar.show()
, the closure is also handled with a special bytecode:
>>> dis.dis(c.show)
5 0 LOAD_DEREF 0 (i)
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
The LOAD_DEREF
opcode loads the cell at index 0
just as I did above.
Upvotes: 2
Reputation: 6013
This is normal behavior. The class that you defined will work like a closure function: it will have access to its enclosing scope -- in your case the foo
function.
You can refer to the official documentation on scopes and namespaces.
Upvotes: 0