Python陷阱
本文整理了一些平常使用python时碰到的陷阱或错误
全局变量v.s.局部变量
global定义位置
global a
a=1
def func():
a+=1
print(a)
func()
a+=1
print(a)
File "XXX", line 9, in
func()
File "XXX", line 6, in func a+=1
UnboundLocalError: local variable 'a' referenced before assignment
原因:当已经在函数体外的变量已经被定义为全局变量后,在函数体内又被重新赋值一遍,这时的变量在函数体内又被定义为局部变量,只在函数体内才会有效。在函数体外的时候恢复之前未在函数体内定义的状态。[1]
修改
a=1
def func():
global a
a+=1
print(a)
func()
a+=1
print(a)
输出:
2
3
对比
global a
a=1
def func():
b=a+5
print(b)
func()
a+=1
print(a)
输出:
6
2
• Python 的全局变量是模块 (module) 级别的 • 每个python函数拥有对应的`__globals__`字典,该字典与函数所属模块的`__dict__`字典完全相同。函数的全局变量也会从这个字典中获取。 • 避免全局变量将使得程序更容易被调试,同时也能提升程序的可读性
模块1--foo.py|模块2--bar.py -|-
def f():
print(a)
def main():
global a
a = 5
f()
main()
from foo import f
def main():
global a
a=4
f()
main()
这是因为a
被定义在foo.py
的main()
函数中,而当导入f()
函数时,foo.py
的main
函数并未被运行,所以a
也没有被定义。
反汇编f()
import dis
from foo import f
dis.dis(f)
输出:(f()函数的字节码)
行号 | - | - |
---|---|---|
2 | 0 LOAD_GLOBAL | 0 (print) |
- | 2 LOAD_GLOBAL | 1 (a) |
- | 4 CALL_FUNCTION | 1 |
- | 6 POP_TOP | - |
- | 8 LOAD_CONST | 0 (None) |
- | 10 RETURN_VALUE | - |
从反汇编可以看出变量a
被认为是全局变量。Python中的每一个函数都拥有一个__globals__
字典变量,该变量实际是函数所属模块的__dict__
变量的引用。所以在bar.py
中我们想在bar.main
函数中将全局变量a
赋值为4,实际改变的是bar.py
的__dict__
字典变量 (注:而不是定义f
的foo.py
的__dict__
字典变量)
mutable对象
用mutable对象作默认参数
本节参考博文:程序员必知的Python陷阱与缺陷列表[2]。Python和其他很多语言一样,提供了默认参数,默认参数确实是个好东西,可以让函数调用者忽略一些细节(比如GUI编程,Tkinter,QT),对于lambda表达式也非常有用。但是如果使用了可变对象作为默认参数,那么事情就不那么愉快了。
这个问题是我在做LeetCode的TwoSum时候发现的。我采用一遍哈希表+尾递归的解法,发现总是思路没问题但结果一直出错(具体解题思路可看我的另一篇博文Two Sum):
One-pass Hash Table + tail recursion
class Solution(object):
def twoSum(self, nums, target,i=0,dict={}):
if dict.get(target-nums[i]) is not None:
return dict.get(target - nums[i]),i
else:
dict[nums[i]] = i
i+=1
return self.twoSum(nums,target,i,dict)
if __name__ == '__main__':
nums = [3,3]
target = 6
sol=Solution()
ans=sol.twoSum(nums,target)
print(ans)
ans2=sol.twoSum(nums,target)
print(ans2)
输出:
[0,1]
[0,0]
Default parameter values are evaluated when the function definition is executed.
stackoverflow上有一个更适当的例子来说明默认参数是在定义的时候赋值,而不是调用的时候。
stackoverflow example
import time
def report(when=time.time()):
return when
report()
report()
输出:
1500113234.487932
1500113234.487932
A way around this is to use None as the default, and explicitly test for it in the body of the function
mutable object solution
import time
def report(when=None):
if when is None:
when = time.time()
return when
report()
report()
输出:
1500113234.487932
1500113448.552873
Python的float除法和整除法
不同版本的python对整数/整数
的处理不同:
教老版本的python:整数/整数
---整除法
较新版本的python:整数/整数
---float除法
最保险的做法应明确告诉编译器你想要的结果,即:
整除法:用//
,e.g.数//数
float除法:用/
且除数与被除数中有一个数是浮点数,e.g.float/数
或数/float