2.6 可变对象和不可变对象
Python的数据类型分为可变类型和不可变类型,可变类型的对象的值是可以被修改的,称为可变对象,而不可变类型的对象的值是不可修改的,称为不可变对象。数值类型、str类型、tuple类型都是不可变类型,而list类型、set类型、dict类型等都是可变类型。例如,一个list对象的值是可以修改的,即可以对其中的元素进行修改、删除、添加等操作。例如:
a=[1,2,3] a[0]=4 print(a)
输出:
[4, 2, 3]
可以看到,变量a引用的list对象的第1个元素被修改了。因为tuple对象是不可修改的,下面代码将产生错误:
t=(1,2,3) t[0]=4
产生了类型错误“TypeError: 'tuple' object does not support item assignment”。
但下面代码不会有任何错误:
t=(1,2,3) print(t) t=(4,5,6) print(t)
输出:
(1, 2, 3) (4, 5, 6)
上述代码中的变量名分别指向了不同的tuple对象,而它们指向的tuple对象本身并没有被修改。
前面讲过,变量是对象的引用,也就是说,变量仅是对象的别名,而不是对象本身。当变量t引用不可修改的tuple对象(1,2,3)时,这个tuple对象不可修改,但可以对变量名t重新赋值,如“t=(4,5,6)”使变量t又引用了这个新的tuple对象。
可以通过下面代码进一步理解上述内容:
x="hello" y=x print(x is y) #x, y指向同一个不可修改对象hello print(id(x), id(y)) x="world" #x引用了新对象world,而并未修改对象hello print(y) #y仍然引用的是对象hello print(x) #x引用的是新对象world print(id(x), id(y))
输出:
True 17939718919841793971891984 hello world 17939718914801793971891984
可以看到,变量x前后指向的是不同的对象,而并未修改变量x指向的对象。
同样,执行复合赋值语句,如“x+=1”等,并不修改x指向的对象,而是让x指向新创建的对象。例如:
x=2 y=x #y和x指向了同1个对象2 print(id(x), "\t", id(y)) x+=1 #x+1等价于x=x+1。因为x指向的int对象是不可修改的(immutable) #用x+1的结果给x赋值,就是让x变量名指向这个新的结果值对象 print(x) print(y) print(id(x), "\t", id(y))
其中,“x+=1”使得x引用了新对象,而并没有修改原来x引用的不可变对象“2”。
输出:
1717202432 1717202432 3 2 1717202464 1717202432
如果1个变量指向的是1个可修改的(mutable)对象,那么可以通过这个变量修改它指向的对象,而这个时候并不会创建新对象。例如:
a=[2,3] b=a #b和a都是list对象[2,3]的引用 a[0]=4 #修改a指向的list对象的第1个元素 print(a) print(b) print(id(a)) print(id(b)) b[1]=7 print(a) print(b)
输出:
[4, 3] [4, 3] 2214058333320 2214058333320 [4, 7] [4, 7]
可以看出,上述代码修改a、b指向的list对象的元素,并没有修改a、b本身。因此,a、b始终指向的是同1个对象,无论变量指向的是1个可修改的对象还是不可修改的对象,给这个变量赋值都会使这个变量引用其他的对象。例如:
a=[2,3] print(id(a)) a=[4,5] print(id(a))
输出:
1810380911112 1810363678280
再看下面的代码:
a=(1, [2,3], 4) b=a #a[1]=20 #错:元祖a的元素是不可修改的 print(a[1]) print(id(a[1])) a[1][0]=20 #a[1]元素是一个list对象,是可修改的 print(a[1]) print(id(a[1]))
因为元组a是不可修改的,即不可以修改其内容如元素a[1]的值,即“a[1]=20”将出错,但可以修改a[1]引用的那个可变list对象[2,3]。因此,“a[1][0]=20”是可以修改的。通过输出a[1]的id值,可以发现,作为元素a的元素,a[1]的值没有改变,仍然引用的是原先的list对象,但其引用的list对象内容发生了变化(通过打印a[1]可知道这一点)。以上修改a[1]引用的list对象的。过程如图2-2所示。
图2-2 修改a[1]引用的list对象
总结
● 数据类型分为可变类型和不可变类型,可变类型的对象的值是可修改的,而不可变类型的对象的值是不可修改的。
● 无论变量指向的是一个可修改的对象,还是一个不可修改的对象,给这个变量赋值都会使这个变量引用其他的对象(如创建的新对象)。对变量指向的可变对象的值进行修改不会使变量引用新对象,只是修改变量引用的对象的值。