Python陷阱–参数-可变类型
1 前言
当方法参数为可变类型时,可能会导致预期之外的执行结果。这种不会抛出异常的错误,需要我们日常积累经验、提升对python语言的理解,从而去避免错误的发生。
举个栗子
def function(li = []):
li.append(1)
print(li)
function()
function()
[1]
[1, 1]
是不是和其他语言不同,导致这一切发生的原因在于,Python是解释性语言,其形参在方法定义时就已分配内存空间,所以对于可变型数据而言,方法的两次调用过程中,形参指向同一内存单元。
2 分析
先看可变类型的分析:
from random import choice
def function1(li = []):
print("The id of li is:", id(li))
li.append(choice([[2], [22], [222]]))
print("The id of li is:", id(li))
print("The value of li is:",li)
The id of li is: 140338997097608
The id of li is: 140338997097608
The value of li is: [[222]]
The id of li is: 140338997097608
The id of li is: 140338997097608
The value of li is: [[222], [222]]
对于可变类型,形参li
存储的其实是一个列表的引用,列表是可变类型,当其内容变化时li
中存储的引用(即地址值)不会变化,故而下次调用方法function1时,li指向的是上次修改过后的列表,即li=[1],不再是空列表了。
不可变类型分析:
from random import choice
def function2(string = ""):
print("The id of string is:", id(string)) #修改前id
string += choice(["2", "22", "222"])
print("The id of string is:",id(string)) #修改后id
print("The value of string is:",string)
The id of string is: 139992122702512
The id of string is: 139992121910248
The value of string is: 2
The id of string is: 139992122702512
The id of string is: 139992121026688
The value of string is: 22
对于不可变类型如字符串,第一次执行function2时,字符串的值发生改变,所以string指向另一块存储空间**[2]。与可变类型参数的另一点不同在于,第二次执行方法function2时,参数string**指向初始的内存空间。具有“记忆性”,这一点与其他语言类似。
对于函数function2有一个有趣的现象,上面两次执行结束后字符串不同(“2"和"22”),若字符串相同比如均为"22"那么,出于底层的优化效果二者id值相同。类似的python中所有[-5, 256]的整数,其id均相同,一种优化(有兴趣的话了解一下时间、空间相似性)。
3 小结
简言之,可变类型做参数时,由于前后调用过程中指向同一块内存空间(id值不变),所以其修改效果会”保留“。