本文整理一些平常使用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
跨模块使用global
跨模块使用global会更容易带来复杂且难以理解的陷阱。该节参考的是punchagan的博文2以及对应的译文3。
• 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()
运行--foo.py|运行--bar.py
输出正常结果5|报错:NameError
File "XXX", line 13, in
main()
File "XXX", line 9, in main f()
File "XXX", line 5, in f print(a)
NameError: global name 'a' is not defined
这是因为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陷阱与缺陷列表4。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]
一开始以为是形参和实参的问题,后来男票提醒才知道是mutable对象的问题。python中一切都是对象,函数也不列外,默认参数只是函数的一个属性。而默认参数在函数定义的时候已经赋值了。
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
python docoment 给出了标准的解决办法:
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
mutable对象和(x+=y v.s. x=x+y)
待更新。。。
本文版权归作者AmyZhuang(博文地址: https://www.cnblogs.com/weixia14/) 所有,欢迎转载和商用,请在文章页面明显位置给出原文链接并保留此段声明,否则保留追究法律责任的权利,其他事项,可留言咨询。