1.引用
python中变量与对象之间的细节。(或者说 引用和对象分离 )
在python中,如果要使用一个变量,不需要提前进行声明,只需要在用的时候,给这个变量赋值即可。
例1:
a=1
这是一个简单的赋值语句,其中整数1为一个对象,a是一个引用,利用赋值语句,引用a指向了对象1。可以通过python的内置函数id()来查看对象的内存地址。
例2:
a=2
print id(a) #24834392
a='banana'
print id(a) #139990659655312
第一个语句中, 2是储存在内存中的一个整数对象,通过赋值 引用a 指向了 对象 1;
第二个语句中,内存中建立了一个字符串对象‘banana’,通过赋值 将 引用a 指向了 ‘banana’,同时,对象1不在有引用指向它,它会被python的内存处理机制给当我垃圾回收,释放内存。
例3:
a=3
print id(a) #10289448
b=3
print id(b) #10289448
可以看到 这俩个引用 指向了同一个 对象,这是为什么呢? 这个跟python的内存机制有关系,因为对于语言来说,频繁的进行对象的销毁和建立,特别浪费性能。所以在Python中,整数和短小的字符,Python都会缓存这些对象,以便重复使用。
(具体详见python知识点之垃圾回收机制)
例4:
a=4
print id(a) #36151568
b=a #引用b指向引用a指向的那个对象
print id(b) #36151568
a=a+2
print id(a) #36151520
print id(b) #36151568
第3句对 a 进行了重新赋值,让它指向了新的 对象6,虽然a 的引用改变了,但是 b 的引用未发生改变,a,b指向不同的对象。
可以得到,即使是多个引用指向同一个对象,如果一个引用值发生变化,那么实际上是让这个引用指向一个新的引用,并不影响其他的引用的指向。从效果上看,就是各个引用各自独立,互不影响。
例5:
引用又分为指向可变对象(如列表)和指向不可变对象(数字、字符串、元祖)。
L1=[1,2,3]
L2=L1
print id(L1) #1396430512
print id(L2) #1396430512
L1(0)=10
print id(L1) #1396430512
print id(L2) #1396430512
print L2 #[10,2,3]
与例4相同都修改了其中一个对象的值,但是可以发现 结果 并不相同。L1 和 L2 的引用没有发生任何变化,但是 列表对象[1,2,3] 的值 变成了 [10,2,3](列表对象改变了)
在该情况下,我们不再对L1这一引用赋值,而是对L1所指向的表的元素赋值。结果是,L2也同时发生变化。
对比例4以及例5可得,引用不可变对象,不能改变对象自身,只是改变引用的指向。引用可变对象,赋值操作可直接改变引用的对象自身(即修改对象的值)。
列表可以通过引用其元素,改变对象自身(in-place change)。这种对象类型,称为可变数据对象(mutable object),词典也是这样的数据类型。 而像之前的数字和字符串,不能改变对象本身,只能改变引用的指向,称为不可变数据对象(immutable object)。
判断两个引用所指的对象是否相同,可用is关键字:
is是通过对比内存地址(id)来判断的,返回True 或False。
(扩充:python对象有三要素:id、type、value,
is 用id 判断 , == 用 value判断 )
2.拷贝
浅拷贝(copy):拷贝一个对象,但是对象的属性依然引用原来的,即增加了一个指针指向已经存在的内存。
假设原对象”will”,由于浅拷贝”will”会创建一个新的对象”willber”,所以”will”的 id和”willber”的id不同。(即”wilber is not will”)但是,对于对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址),也就是说”wilber[i] is will[i]”。具体原始对象修改元素如何反映在拷贝后对象上见例6。
深拷贝(deepcopy):增加一个指针并且申请一个新的内存,使这个增加的指针指向新的内存。
例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','b']数组对象
print a #[1,2,3,4,['a','b','c'],5]
print b #[1,2,3,4,['a','b','c'],5]
print c #[1,2,3,4,['a','b','c']]
print d #[1,2,3,4,['a','b']]
首先,a为可变对象的引用,所以修改a,可变对象的值也随之改变,a和b指向同一个对象,所以a、b值相同。
其次,c是a 的浅拷贝,浅拷贝的各个元素整体上改变是没有影响。但是仅元素部分修改是互相牵制的。若修改的元素是不可变类型,比如a[0],则a对应的list的第一个元素会使用一个新的对象,而c依然指向原始对象;若修改的元素是可变类型,比如a[4],修改操作不会产生新的对象,所以a的修改结果会相应的反应到c上。
最后,d为深拷贝,有新的内存存储原始对象,所以不改变。
3.引用问题在函数以及类、实例上的使用:
3.1函数的参数传递
a = 1
def fun(a):
a = 2
fun(a)
print a
a = []
def fun(a):
a.append(1)
fun(a)
print a # [1]
当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了。
所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉。
而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改。
3.2.实例调用类变量
class Person:
name="aaa"
p1=Person()
p2=Person()
p1.name="bbb"
print p1.name # bbb
print p2.name # aaa
print Person.name # aaa
类变量就是供类使用的变量,实例变量就是供实例使用的.
这里p1.name=”bbb”是实例调用了类变量,这其实和上一个问题一样,就是函数传参的问题,p1.name一开始是指向的类变量name=”aaa”,但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了.
class Person:
name=[]
p1=Person()
p2=Person()
p1.name.append(1)
print p1.name # [1]
print p2.name # [1]
print Person.name # [1]
引用部分参考网址https://www.cnblogs.com/ShaunChen/p/5656971.html
参考http://blog.csdn.net/u013510614/article/details/50751017