最近看到关于python中不可变(immutable)类型的问题,说int、float、string、tuple都是不可变类型,我的第一个理解是,python中不可变类型就是:相同值则同对象,不同值不同对象,变量值改变则不再指向原对象;事实证明,这个理解理想化了,和实际情况不符。
第一个小实验看不出问题:
x = y = 1
x = 2
print(x,y,id(x),id(y))#两者地址不等
y = 2
print(x,y,id(x),id(y))#两者地址相等
#这说明在python中,整数1,整数2,分别是两个不同的对象,而两个整数2是同一个对象
x = y = [1]
x[0] = 2
print(x,y,id(x),id(y))#值相等,地址相等,由于x、y和同一个对象绑定
x = [1]
y = [1]
print(x,y,id(x),id(y))#值相等,地址不相等,分别是不同的对象
#证明list可变类型的特点,同值也不是同一个对象
第二个小实验出现的情况更坚定了开头的看法:
def foo(x):
print('id(x)=',id(x))#此处x的地址还和a一样,整数的话,值一样,就是同一个对象
x += 5
print('id(x`)=',id(x))#此处x的地址发生了变化,因为值改变
x -= 1
print('id(x``)=',id(x))#地址又发生了变化
#如果此时给x赋值82,y的地址就会和x`一样
return x
a = 77
print('id(a)=',id(a))
y = foo(a)
print('a=',a,',y=',y)
print('id(y)=',id(y))
第三个则出现很多意外:
a = "asdf"
b = "ghjk"
c = "asdf"
print('id(a)=',id(a),'id(b)=',id(b),'id(c)=',id(c))
#a、c地址相同
a = 2.0
b = 2.001
c = a + 0.001
d = 2.001
print(a,'id(a)=',id(a),b,'id(b)=',id(b),c,'id(c)=',id(c),d,'id(d)=',id(d))
#b、d地址相同,但是c不是,而b、c、d值相同,此时还以为是浮点误差导致
a = (1,2)
b = (1,2)
r = 2
c =(1,r)
print(a,'id(a)=',id(a),b,'id(b)=',id(b),c,'id(c)',id(c))
#三者值相同,地址都不同,说明tuple的“不可变”说的是不让改的意思。
a = (1,2)
print(a,'id(a)=',id(a))
#a又出现第四个地址
a = "asdf"
b = "asd"
print(a,b,'id(a)=',id(a),'id(b)=',id(b))
b += 'f'
print(a,b,'id(a)=',id(a),'id(b)=',id(b))#此时a、b值相同,地址不同
#很遗憾,这里b不会再变回和a一样的地址,即便现在值相同,说明是否是相同的字符串对象看的是初值
#b的地址变得和"asd"、"asdf"都不一样,也就是变成了新的对象,和list也不同
c = "apple"
f = "maple"
print(c,id(c))
d = c.replace('f','t')
e = c.replace('ap','ma')
print(d,id(d),e,id(e),f,id(f))#c、d地址相同,并没有建立新对象
#多次运行,apple的地址不变,maple则总是改变,也都和来自初值maple的不一样,d则不变
有人在某些条件下试出来string类型在+=后id不变。
(https://web.eecs.utk.edu/~azh/blog/pythonstringsaremutable.html)
暂时的结论就是,int最接近于开头的理解,float、string则只在初值的地方成立。等到tuple只是不许修改的“不可变”。