本篇文章讲了两个方面。变量区域问题
python 可变变量, 不可变量
自己用python 好长时间了,但是还是没搞懂python中的一些传递参数问题。
我们先来看个问题,比如:
num = 0
def helper():
print(num)
num += 1
helper()
会出现报错信息:UnboundLocalError: local variable 'num' referenced before assignment
说之前没有定义num,但是我们这样就为什么就可以正常输出?
num = 0
def helper():
print(num)
helper()
这个问题很好解释的,就是说当你print(num),系统去找它,发现自己局部变量没有它,接下来就去上一层找,发现了!便可输出num,然而当你操作num + 1时候,局部要改变全局变量num系统当然不干了!(只能用不能修改)
但是,如果按照下面的写法
num = 0
def helper():
num = 0
num += 1
print(num)
helper()
print(num)
输出为
1
0
其实这两个num其实没有半毛钱关系,但是num = 0,它们地址又一样,咋回事?。我们打印此时num的id(地址)
num = 0
print("out num", id(num), num)
def helper():
num = 0
print("in num", id(num), num)
num += 1
print("in num", id(num), num)
helper()
print("out num", id(num), num)
输出:
out num 140716149232640 0
in num 140716149232640 0
in num 140716149232672 1
out num 140716149232640 0
num = 0地址是一样的。 为什么呢?原因是:地址数据最少是32位的,现在都是64位了,如果单独为一个小整数创建一个对象,10个地方用到这个小整数,那么就会在内存中创建10个存储的内存地址的空间,地址占用的数据长度比数据本身还大这样非常不划算。而有这个缓存池,python解释器内部就会共享这个小整数对象,不去开内存空间。从而减少内存的使用率,降低浪费。
小整数对象池:python在执行的时候,为了节约空间,帮我们创建好了小整数对象池,[-5~256],都是固定的地址,不管你用不用,都会存在。比如,a=5,b=5,id(a)和id(b)的地址都是小整数池中固定已经存在的地址,所以相等但如果,a=1000,b=1000,这时会给a一个地址,也会给b一个地址,但他们都不相等。
所以,我们完全可以认为它们是俩个不同地址的变量。
好了, 你说没有关系,那么我把num参数传递过去,这个总应该有关系了吧!我们通过参数传递时候,又有一个问题,
num = 0
arr = [1]
def helper(num,arr):
num += 1
arr.append(1)
print("in", num, arr)
helper(num, arr)
print("out", num, arr)
输出为:
in 1 [1, 1]
out 0 [1, 1]
为什么我用 helper改变全局的num和 arr只有arr改变了,num没有改变。
为了解释问题,这个我们需要了解python的可变,不可变对象 和 传递参数,这三个名词!
第一 ,不可变对象(变量指向的内存的中的值不能够被改变):不可变对象分为 int,string,float,tuple
比如:
num = 1
print(id(num))
num += 1
print(id(num))
输出:
140716149232672
140716149232704
地址发生了改变,值当然也改变了!
第二, 可变对象(变量指向的内存的中的值能够被改变):可变对象:list, dict
arr = [1]
print(id(arr))
arr.append(1)
print(id(arr))
输出
1955375811336
1955375811336
仍然地址不变,但是值改变了!
第三, python参数传递都是传递引用,也就是传递给函数的是原变量实际所指向的内存空间,修改的时就会根据该引用的指向去修改该内存中的内容,所以按道理说我们在函数内改变了传递过来的参数的值的话,原来外部的变量也应该受到影响。但是上面我们说到了python中有可变类型和不可变类型,这样的话,当传过来的是可变类型(list,dict)时,我们在函数内部修改就会影响函数外部的变量。而传入的是不可变类型时在函数内部修改改变量并不会影响函数外部的变量,因为修改的时候会先复制一份再修改。
这样大家理解,为什么 num 没有改变, 而 arr改变了!
所以,如果我们用不可变类型作为参数传递过去, 是不会改变它全局的值的!
但是,我非要想用helper()改变num值,怎么办,这里可以用关键字 global,即
num = 0
def helper(n):
global num
if n == 0: return 1
num += n
return helper(n - 1)
print(num)
helper(3)
print(num) // 输出: 0 6
还有,我不想改变 arr,怎么办?只能重新开辟地址
arr1 = [1]
arr2 = arr1
arr2.append(2)
print("arr1", arr1,"arr2", arr2)
print("--------------")
arr3 = arr1[:]# arr3 = arr1.copy() 或者 arr2 = list(arr1)
arr3.append(3)
print("arr1", arr1, "arr3", arr3)
输出:
arr1 [1, 2] arr2 [1, 2]
--------------
arr1 [1, 2] arr3 [1, 2, 3]
大家还有什么疑问,欢迎留言,大家一起讨论!
参考资料: