python中对象、传递与拷贝
python对象
Python 中,一切皆对象。每个对象由:标识(identity)、类型(type)、value(值)组成。
标识用于唯一标识对象,通常应用于对象在计算机内存地址。使用内置函数id(obj)可返回对象obj的标识。
python的传递
python中的传递是引用传递。变量就是对象的引用,变量中存着对象的内存地址。
python是动态语言。变量不需要显示声明类型(type),根据变量引用的对象,python解释器自动确定数据类型。
python是强类型语言,每个对象都有数据类型,并只支持该类型支持的操作。
变量存放在栈中,栈中存放的obj的内存地址。而obj存放在堆中。堆中的这个obj存着:value、type、id(内存地址)。
不可变对象
在不可变对象中,变量和对象是互相独立的,当新建一个变量指向对象是不会对对象有任何影响。
a = "123" b = a a = "xyz" print (a) print (b)
这个打印结果就是"xyz" "123"。对照上面的内存图。这就解释了为什么说python的变量是个标签。当b=a , a=xyz时,a这个变量就跟标签一样就贴在了"xyz"这个对象上(实质上是新的内存地址赋给了a)。由原来"123"有两个标签a和b,变成了b指向123,a指向新的对象xyz。这些前提一定要基于对象是不可变对象(strings, tuples, 和numbers)。
可变对象
在可变对象(list,dict,set)中,如果会直接在当前对象进行操作。并不会为新的变量进行副本拷贝。
a = [1, 2, 3] b = a a[0], a[1], a[2] = 4, 5, 6 //改变原来 list 中的元素 >>> a [4, 5, 6] >>> b [4, 5, 6]
浅拷贝和深拷贝
浅拷贝
对于不可变类型,浅复制仅仅对内存地址进行复制,不会开辟新的内存空间。
对于可变类型,浅复制会开辟新的内存空间(仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的)。子序列中的元素地址还是一样的(新旧对象子序列是共享的)。
浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。
深拷贝
浅拷贝,除了顶层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝)。
经过深拷贝后,原始对象和拷贝对象所有的元素地址都没有相同的了(对于不可变对象指向相同的地址空间,可变对象序列内存地址就不同了)。
import copy a = [1, 2, 3, 4, ['a', 'b']]#原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy.deepcopy(a) #对象拷贝,深拷贝 a.append(5) #修改对象a a[4].append('c') #修改对象a中的['a', 'b']list对象 print ('a = ', a) print ('b = ', b) print ('c = ', c) print ('d = ', d) print(id(a)) print(id(c)) print(id(d))
结果:
当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛钱关系。
函数内的引用指向的是可变对象,对它的操作就和定位了指针一样,在内存里进行修改。
如果两个对象是同一个类的实例对象,使用 is 和 == ,结果都是false。但是实例对象里的值是相等的,这时候可以重载运算符来解决这个问题。