一、可变类型与不可变类型
(1)可变类型(mutable):列表、字典、集合
(2)不可变类型(unmutable):数字、字符串、元组
简单点说:可变对象就是允许对自身进行修改;不可变对象不允许对自身进行修改,如果修改了就不是原来的对象了,我们可以用内置函数 id() 来判断!!!
注意:这里的可变不可变指的是内存中的那块内容(value)是否可以被改变。如果是不可变类型的话,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,在对对象操作的时候,不需要在其他地方申请内存,只需要在此对象后面连续申请(+ / -)即可,也就是它的address会保存不变,但是区域会边长或变短。
可以使用内置函数 id() 来确认对象的身份在两次赋值前后是否发生变化。
不可变类型有什么好处?如果数据是不可变类型,当我们把数据传给一个不了解的API时,可以确保我们的数据不会被修改。如果我们要操作一个从函数返回的元组,可以通过内置函数 list() 把它转换成一个列表。(当被问到列表和元组的区别时,可以说这个!!!)
二、== 与 is 在对象间的比较
(1) == 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。默认会调用对象的__eq__()方法
(2)is 比较的是两个实例对象是不是完全相同,他们是不是同一个对象,占用的内存地址是不是一样的。即比较id是否相同
简单点来说:
(1) == 比较操作符是用来判断两个对象的 value是否相等。
(2) is 同一性操作符是用来判断两个对象的 id是否相同
(3)注意:is 运算符比==效率高,在变量和None进行比较时,应该使用is
例子:
>>> a = 1 #a和b为数值类型 >>> b = 1 >>> a is b True >>> a == b True >>> a = 257 #a和b为数值类型 >>> b = 257 >>> a is b False >>> a == b True >>> a = 'python' #a和b为字符串类型 >>> b = 'python' >>> a is b True >>> a == b True >>> a = (1,2,3) #a和b为元组类型 >>> b = (1,2,3) >>> a is b False >>> a == b True >>> a = [1,2,3] #a和b为list类型 >>> b = [1,2,3] >>> a is b False >>> a == b True >>> a = {'a':1,'b':2} #a和b为dict类型 >>> b = {'a':1,'b':2} >>> a is b False >>> a == b True >>> a = set([1,2,3])#a和b为set类型 >>> b = set([1,2,3]) >>> a is b False >>> a == b True
注意:python仅仅对比较小的整型对象进行缓存(范围是[-5 , 256]),而不是所有的整型对象。需要注意的是,这仅仅在IDLE命令行中执行才是这样子的结果,如果在pycharm或者保存为文件执行时,结果是不一样的!这是因为解释器做了一系列的优化。同样的道理,字符串对象也有类似的缓存池,超过这个区间范围自然就不会相等。
总的来说,只有数值型和字符串型,并且在通用的对象池中的情况下,a is b才为True 。否则当a和b是int 、 str 、tuple 、 list 、 dict 、 set时,a is b为False
三、变量赋值、浅拷贝与深拷贝
A、变量赋值
在python中,都是将“对象的引用(内存地址)”赋值给变量的。
直接赋值,传递对象的引用而已,原始列表改变,被赋值的b也会做相同的改变。
>>> list = [1,2,3] >>> a = list >>> print(a) [1, 2, 3] >>> list.append(4) >>> print(a) [1, 2, 3, 4] >>> x = 2 >>> y = x >>> print(id(x)) 140712489444208 >>> print(id(y)) 140712489444208 >>> print(id(2)) 140712489444208
在语句x = 2中,做了两件事:创建了一个整型对象;将该对象的引用(即对象的内存地址)赋值给名为x的变量,创建变量x。
注意:
(1)在python中,每个变量在使用前都必须赋值,变量是在赋值的那一刻被创建的。
(2)在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象的类型。
有人喜欢把给变量赋值比喻成“贴标签”,把x, y这两个标签贴到2这个对象上。
B、浅拷贝 -- copy模块里面的copy方法实现
copy浅拷贝,没有拷贝子对象,所以原始数据改变,子对象会改变
(1)对于 不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间。
(2)对于 可 变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝。
(3)浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;
(4)浅拷贝后,改变原始对象中为不可变类型的元素的值,只有原始类型受影响。 (操作拷贝对象对原始对象的也是同理)
可变类型跟不可变类型在浅拷贝中的区别: (1)不可变类型 数字、字符串、元组 -------地址相同!!! >>> import copy >>> num1 = 1 >>> num2 = copy.copy(num1) >>> print(id(num1)) 140712489444176 >>> print(id(num2)) 140712489444176 >>> str1 = 'happy' >>> str2 = copy.copy(str1) >>> print(id(str1)) 2983938062072 >>> print(id(str2)) 2983938062072 >>> tup1 = (1,2,3,'AI') >>> tup2 = copy.copy(tup1) >>> print(id(tup1)) 2983937738936 >>> print(id(tup2)) 2983937738936 (2)可变类型 列表、字典、集合 ------ 地址不同!!! >>> import copy >>> list1 = [1,2,3] >>> list2 = copy.copy(list1) >>> print(id(list1)) 2983938304456 >>> print(id(list2)) 2983938304840 >>> dict1 = {'A':1, 'B':2} >>> dict2 = copy.copy(dict1) >>> print(id(dict1)) 2983938292112 >>> print(id(dict2)) 2983938001800 >>> set1 = {1,2} >>> set2 = copy.copy(set1) >>> print(id(set1)) 2983938033928 >>> print(id(set2)) 2983938032808 (3)注意 copy浅拷贝,没有拷贝子对象,所以原始数据改变,子对象会改变 : >>> import copy >>> alist = [1,2,3,['a','b']] >>> c = copy.copy(alist) >>> print(alist) [1, 2, 3, ['a', 'b']] >>> print(c) [1, 2, 3, ['a', 'b']] >>> alist.append(5) # 此操作不会改变浅拷贝c的值 >>> print(alist) [1, 2, 3, ['a', 'b'], 5] >>> print(c) [1, 2, 3, ['a', 'b']] >>> alist[3] ['a', 'b'] >>> alist[3].append('c') # 此操作会改变浅拷贝c的值 >>> print(alist) [1, 2, 3, ['a', 'b', 'c'], 5] >>> print(c) [1, 2, 3, ['a', 'b', 'c']] >>> id(alist) 1426982476744 >>> id(c) 1426982477128
注意:会产生浅拷贝效果的操作:
(1)切片操作
(2)使用工厂函数(如list/dir/set)
(3)使用copy模块中的copy()函数
C、深拷贝--copy模块里面的deepcopy方法实现
深拷贝,包含对象里面的自对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变
注意:字典自带的copy()方法可实现深拷贝
注意:在六种基本类型中,深拷贝与浅拷贝的影响几乎是一致的。因为深拷贝对比浅拷贝,强调的是递归,强调的是资源数,对于对顶层的操作,深拷贝与浅拷贝无异!
注意:深拷贝,包含对象里面的自对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变 >>> import copy >>> d = copy.deepcopy(alist) >>> print(alist) [1, 2, 3, ['a', 'b', 'c'], 5] >>> print(d) [1, 2, 3, ['a', 'b', 'c'], 5] >>> alist.append(6) # 不会对深拷贝的d造成影响 >>> print(alist) [1, 2, 3, ['a', 'b', 'c'], 5, 6] >>> print(d) [1, 2, 3, ['a', 'b', 'c'], 5] >>> alist[3].append('d') # 不会对深拷贝的d造成影响 >>> print(alist) [1, 2, 3, ['a', 'b', 'c', 'd'], 5, 6] >>> print(d) [1, 2, 3, ['a', 'b', 'c'], 5] >>> import copy >>> l1 = [1,2] >>> l2 = [3,4] >>> num = 5 >>> allone = [l1,l2,num] >>> print(allone) [[1, 2], [3, 4], 5] >>> allone2 = copy.deepcopy(allone) >>> print(allone2) [[1, 2], [3, 4], 5] >>> allone[1] = [4,3] >>> allone[2] = [6,5] >>> print(allone) [[1, 2], [4, 3], [6, 5]] >>> print(allone2) # 依旧不会对深拷贝的d造成影响 [[1, 2], [3, 4], 5]
D、python默认的拷贝方式为浅拷贝
时间角度:浅拷贝花费时间更少;
空间角度:浅拷贝花费内存更少;
效率角度:浅拷贝只拷贝顶层数据,一般情况下比深拷贝效率高。