我在python方法中定义了一个类,如下所示
c = None
def foo():
i = 1
class bar( object ):
def show( self ):
print i
global c
c = bar()
foo()
print c.show() #prints 1
上面的代码打印1,它是i
的值。
' i'存储为bar的实例访问?使用c.__class__()
在课堂外创建的实例也可以访问i
答案 0 :(得分:3)
您通过{{3}}访问变量。在python2.x中,i
的值实际存储在show
1 属性中的func_closure
函数对象中:
>>> 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
通过查看反汇编的字节码,我们可以获得更多的洞察力:
>>> 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
啊,所以看起来像是通过闭包访问的对象是由LOAD_DEREF
OP代码加载的,它也告诉python要查看哪个单元格(在本例中为0)。
1 __closure__
和__func__
分别是func_closure
和im_func
的python3.x名称。它们在python2.6中有别名,以帮助编写前向兼容代码。
答案 1 :(得分:2)
您正在访问关闭; i
的嵌套范围将关闭bar.show()
。没有类的嵌套函数使用的变量也会发生同样的情况。
你可以通过反省方法看到闭包;我已经改变了你的代码,只是简单地返回了类,因为它简化了事情:
>>> 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
因此c.show
是绑定方法,c.show.__func__
是原始show
函数,c.show.__func__.__closure__
是闭包单元格的元组。我已使用closure.cell_contents
属性访问了单元格的当前值。
父函数foo
也知道必须保留变量i
,Python在函数的代码对象中记录了该信息:
>>> foo.__code__.co_cellvars
('i',)
并且函数的字节码创建一个闭包单元作为代码的一部分:
>>> 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
MAKE_CLOSURE
和LOAD_DEREF
opcode字节代码一起生成闭包单元格并将其传递给将生成类的代码对象(由索引21处的LOAD_CONST
字节码处理) ;类字节码再次将该闭包传递给show
函数:
>>> 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
在bar.show()
内部,闭包也使用特殊字节码处理:
>>> dis.dis(c.show)
5 0 LOAD_DEREF 0 (i)
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
正如我上面所做的那样,closure将索引0
的单元格加载。
答案 2 :(得分:0)
这是正常行为。您定义的类将像闭包函数一样工作:它可以访问其封闭范围 - 在您的情况下是foo
函数。