可变(mutable)和不可变(immutable)参数
不可变对象在进行重新赋值的时候,实际上是将原始值丢弃,将变量指向一个新值;可变对象的可变性质是指更改可变对象的子对象,比如list中的item元素的更改。
Python中的string,tuple(元组)和number是不可变对象,而dict,list等是可变对象;当可变对象传入函数中后,如果在函数中对对象的子对象(子元素)进行修改,那么会导致函数外的原始对象也会进行更改;不可变对象不会存在这个问题。
传递不可变参数示例 调用结果 可变参数函数调用示例 结果可变类型(mutable):列表,字典
不可变类型(unmutable):数字,字符串,元组
不可变类型有什么好处?
如果数据是不可变类型,当我们把数据传给一个不了解的API时,可以确保我们的数据不会被修改。因此,如果我们要操作一个从函数返回的元组,可以通过内建函数list()把它转换成一个列表。
这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(增加内存/减少内存)即可,也就是它的指向内存存储的位置会保持不变,但区域会变长或者变短,可以使用内建函数id()来确认对象的身份在两次赋值前后是否内存的位置是否发生了变化。
内建函数id() 介绍
id(变量)函数,用于返回对象的身份(identity)。其实,这里所谓的身份,就是该对象的内存地址。id地址可理解为相应变量在存储中的位置;这个存储位置不变是指一个运行周期,在一定数值范围内分配的编号,不同运行周期,即便同一变量存储位置都会有变化,可变对象,一个运行周期内,存储位置上的值是可以改变,不可变对象则相反
print(id(a))
print(hex(id(a)))
在我的计算机上,它们返回的是:
11246696
'0xab9c68' 分别为内存地址的十进制和十六进制表示。
单个运行周期内,整数333、111的存储位置已经被分配固定,且存储的内容是不允许修改的,变量a指向了111,因此a的存储位置就是111的位置,b也是如此,当a指向了333,a指向的存储位置就会发生改变,因此a的id值发生了变化。 这里暂时无法给出合理的解释,但会在下面的深浅拷贝中解释部分,并结合自己的理解,同时能力有限,还需要自己进一步理解python的内存管理 列表,字典可变对象进行赋值时,每次赋值都是分配新的地址,从使用角度来讲,是因为这些对象是可变的,如果分配同一地址,当发生修改时,可能导致赋值对象不一致不可变对象赋值,分配的地址是不变,可以理解为他们不需要修改,分配同一物理地址,可以节省内存开销
深浅拷贝的定义
在Python中对象的赋值其实就是对象的引用。当创建一个对象,把它赋值给另一个变量的时候,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已。
浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。也就是,把对象复制一遍,但是该对象中引用的其他对象我不复制
深拷贝:外围和内部元素都进行了拷贝对象本身,而不是引用。也就是,把对象复制一遍,并且该对象中引用的其他对象我也复制。
几种拷贝类型的操作
1、赋值
对不可变对象的赋值
python有一个重用机制,对于同一个数字,python并不会开辟一块新的内存空间,而是维护同一块内存地址,只是将该数字对应的内存地址的引用赋值给变量a和b。所以根据输出结果,a和b其实对应的是同一块内存地址,只是两个不同的引用罢了。同样的,对于c = a,其实效果等同于“c= 1231;”,它也就是将a指向123123的引用赋值给c。字符串跟数字的原理雷同,如果把123123改成“abcabc”也是一样的。
结论:对于通过用 = 号赋值,数字,字符串等不可变对象(元组除外)在内存当中用的都是同一块地址
对元组对象的赋值
结论:对于通过用 = 号赋值 ,每次分配的是不同的物理地址,这和字典,列表的方式是一样的
对可变对象赋值
结论:对可变对象如元组,字典等进行赋值时,实际上是分配不同的物理地址,但list3=list1,list3实际上分配的物理地址是和list1一致的,只是把list1的引用拿过来赋值给list3而已
2、浅拷贝:
进行浅拷贝时,需要引用import copy包,部分对象如列表,字典等是有内置的copy函数的
对不可变对象的浅拷贝
对不可变对象的浅拷贝,实际上只是把a的引用复制给b,a和b的内存地址还是一样的对可变对象的浅拷贝
通过以上结果可以看出,进行浅拷贝时,我们的字典第一层n1和n3指向的内存地址(相当于上图的红色区域)已经改变了,但是对于第二层里的列表并没有拷贝,它的内存地址还是一样的,对于内置函数copy而言,n2和n3在性质上是一样的。这里还要注意最底层的元素,其实物理位置是不变的深拷贝:
进行深拷贝时,需要引用import copy包,部分对象如列表,字典等是有内置的deepcopy函数的
结论: 对于深拷贝,字典、列表、元组等类型,它里面嵌套多少层,就会拷贝多少层出来,但是最底层的数字和字符串地址不变。
切片
总结:
1,切片可以应用于:列表、元组、字符串,但不能应用于字典,切片属于浅拷贝。
2、深浅拷贝,既可应用序列(列表、元组、字符串),也可应用字典。
3、对于不可变对象的深浅拷贝
不可变对象类型,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。
一句话就是,不可变类型,不管是深拷贝还是浅拷贝,地址值和拷贝后的值都是一样的。
4、对可变对象的深浅拷贝
=浅拷贝:值相等,地址相等
copy浅拷贝:值相等,地址不相等
deepcopy深拷贝:值相等,地址不相等
网友评论