Python中的可变与不可变对象
首先要明确一点,在Python中 ,一切赋值或者函数传值的方式都是靠引用。这与C++不同,C++有传值、传引用和传指针。因为Python不能显式的定义数据类型,而且没有指针,所以默认的都是按引用传递。
在Python中,数字、字符串、元组是不可变的,,列表、字典、集合是可变的。可以这么理解,Python是传引用,不可变对象的就是不能改变原来引用的值,那么操作对象的时候,是复制原来的值后,开辟新的内存空间;而可变对象就是在原来的基础上进行更改。
不可变对象
虽然是传递引用,但是由于不能改变原来引用的值,所以只能开辟新的内存空间,在复制原来值的基础上,进行改变。
a=1
b=a # 按引用传递
print(a is b) # 输出 True
b+=1 # 开辟新的空间,在原来的基础上+1
print(a is b) # 输出False
print(a,b) # 1,2
s1="abc"
s2=s1 # 按引用传递
print(s1 is s2) # 输出True
s2+="de" # 开辟新的空间,追加'de'
print(s1 is s2) # 输出False
print(s1,s2) # abc,abcde
可变对象
可变对象可以直接在原来的引用上进行更改,更改的时候不会开辟新的内存空间,是在原来的内存空间上进行处理。
l1=[1,2,3] # 定义列表
l2=l1 # 传引用
print(l1 is l2) # 输出True
l2.append(4) # 原来的基础上追加元素
print(l1 is l2) # 输出True
print(l1,l2) # [1,2,3,4,],[1,2,3,4]
s1={1,2,3,4,5} # 定义集合
s2=s1 # 传引用
print(s1 is s2) # 输出True
s2.remove(1) # 删除1
print(s1 is s2) # 输出True
print(s1,s2) # {1,2,3,4},{1,2,3,4}
# 字典同理,不在编码了
自定义对象的可变性
自定义对象都是可变的类型的。
class Test: # 自定义测试对象
def __init__(self,a=0,b=0):
self._a=a
self._b=b
t1=Test(1,2)
t2=t1 # 传引用
print(t1 is t2) # 输出True
t2._a=0 # 按引用更改
t2._b=0
print(t1 is t2) # True
print("t1:",t1._a,t1._a) # t1:0,0
print("t2:",t2._a,t2._b) # t2:0,0
对象作为函数参数的可变性
可变对象作为函数参数,传入后还是可变对象,相当于传递的引用,函数返回可变对象,返回的是引用。不可变对象传递参数可以理解为按值传递!!
def fun1(a):
a+=1
def fun2(a):
a+=1
return a
def fun3(l):
l.append(2)
def fun4(l):
l.append(4)
return l
a=1
b=a
fun1(a)
print(b is a) # True
print(a) # 1
b=fun2(a) # 2
print(b is a) # False
print(b) # None
l1=[1,2,3]
l2=l1
fun3(l1)
print(l1 is l2) # True
print(l1) # [1,2,3,2]
l2=fun4(l1)
print(l1 is l2) # True
print(l2) # [1,2,3,2,4]
当函数内部新定义了一个参数,同时返回这个参数的时候,是返回函数内部参数的引用,不论这个参数是可变还是不可变的!!!!
def test():
l=[1,2,3]
print(id(l)) # 140482206588104
return l
def test1():
a=1
print(id(a)) # 10919424
return a
l=test()
print(l) # [1,2,3]
print(id(l)) # 140482206588104
b=test1()
print(b) # 1
print(id(b)) # 10919424
在上述代码中可以看出,函数内部的list、数字a与函数外部的list、数字a是同一个!