python变量的思考

   先看一段代码A

1 x=12
2 def foo():
3      #print x
4      x=x+1
5      print x
6 
7 foo()

执行上面的代码会输出什么呢?本人写python代码是还真心没注意过,一般也是把x作为

foo()的参数。如果你执行上面的代码会报错:

UnboundLocalError: local variable 'x' referenced before assignment

不明白的话,看起来确实如此诡异!c语言可不会出现这种情况。如果没有第4行就会正常输出12。

所以问题也就在第4行上。根据报错,大体原因应该知道了,那么我们改写一下上面的代码,代码B

1 x=12
2 print hex(id(x))
3 def foo():
4     x=1
5     print hex(id(x))
6 print x
7 foo()

本人运行输出的是:

0x1d5d6a4

12

0x1d5d728

看以看到id值并不相等,也就是foo函数的x是一个全新的变量,而且作用域仅在函数内。

代码A中x=x+1并不是我们想当然等号右边的x是函数外部的x,这样也不是容易看明白的,

我们使用dis模块反汇编一下。

代码C:

1 import dis
2 x=12
3 def foo():
4     y=x+1
5     print y
6 
7 dis.dis(foo)

运行输出:

  4           0 LOAD_GLOBAL              0 (x)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD          
              7 STORE_FAST               0 (y)

  5          10 LOAD_FAST                0 (y)
             13 PRINT_ITEM          
             14 PRINT_NEWLINE       
             15 LOAD_CONST               0 (None)
             18 RETURN_VALUE        

代码D:

1 import dis
2 x=12
3 def foo():
4     x=x+1
5     print x
6 
7 dis.dis(foo)

运行输出:

  4           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD          
              7 STORE_FAST               0 (x)

  5          10 LOAD_FAST                0 (x)
             13 PRINT_ITEM          
             14 PRINT_NEWLINE       
             15 LOAD_CONST               0 (None)
             18 RETURN_VALUE        

比较一下代码D与代码C的不同,然后再看两段代码的输出。这里说一下,输出的第一列是代码的行号,

第二列是指令在字节码的偏移,第三列指令,第四列示栈的索引序号。

OK,我们看两段代码输出的第一行红色标记的指令,代码C是LOAD_GLOBAL指令,而代码D是LOAD_FAST。

根据手册我们知道:

LOAD_GLOBAL :载入全局co_names[namei]的值到栈上,namei就是此指令后面跟随的值(也就是输出的第四列);

LOAD_FAST   :压入本地co_varnames[var_num]到栈上,var_num是此指令后面跟随的值。

    所以代码C是用全局的x加1,而代码D是利用本地的x加1,但是本地的x没有对应的索引值,必然就错了。

一个变量在另一个作用域中不能做自加等类似的运算,对一个变量赋值相当于c语言中变量的声明并初始化。

上面真正的字节码可以通过foo.func_code.co_code.encode('hex')获得,如代码D的输出:

7c0000640100177d00007c0000474864000053

7c指LOAD_FAST等等...

      再来看另一个例子:

1 x=2
2 print hex(id(x))
3 def foo():
4     x=2
5     print hex(id(x))
6 foo()

输出:

0x9551314
0x9551314

可以看出函数外的2与函数内的2是同一个。
从索引数我们也能看出来:
 1 x=123456789
 2 #print hex(id(x))
 3 print sys.getrefcount(x),
 4 def foo():
 5     x=123456789
 6     #print hex(id(x))
 7     print sys.getrefcount(x),
 8     
 9 foo()
10 print sys.getrefcount(x)
输出:3 4 3
在foo()内时增加了一为4,函数结束又变成了3.
python要学的还很多......
posted @ 2013-12-27 17:20  除e尘  阅读(384)  评论(0编辑  收藏  举报