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要学的还很多......