在方法

时间:2015-09-14 18:38:35

标签: python python-2.7

我在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

3 个答案:

答案 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_closureim_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_CLOSURELOAD_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函数。

您可以参考official documentation on scopes and namespaces