python对象的引用概念_python中的值传递和引用传递(可变对象与不可变对象)也就是赋值的原理-python全部是引用传递...

python中的值传递和引用传递(可变对象与不可变对象)也就是赋值的原理-python全部是引用传递

20141215 Chenxin

猜测:

1.属于同一个类生成的对象,其默认属性指向同一个引用.这样当你修改一个对象的时候,会影响到其他对象,除非你通过类中的其他方法加以修改.实际上应该都是指针指向的概念.

2.基本"变量",就是不可变"对象",是调用的值传递.则当你重新通过"="赋值的时候,python内部是创建了一个新的值(对象)给它.(这里也就是通常所说的值传递).不用关心是哪种传递,说白了,python中都是引用传递.

3.对于"可变对象",如list,dict以及默认对象,完全是引用传递的方式.是内存中一个地址的引用,像指针一样.

4.如果想拷贝一个对象到其他对象(副本形式),那么只能通过python的copy模块来实现.

5.python有个缓存的概念,缓存的整数范围为(-5,256).其他类型的不会缓存.比如x=1,y=1,x is y;x=257,y=257,x is not y; id(*)来检查内存地址.

试验:

关于函数参数传递的解释如下(3个例子):<>P93

def ChangeInt(a):

a=10

b=2

ChangeInt(b)

print b # ->2

翻译为非函数的样子:

b=2

a=b #a的内存地址跟b是一样的,说明这里是内存地址拷贝,因此赋值可以理解为指针指向

a=10 #将a指向了新创建的10对象,a的内存地址发生了改变.

print b

def ChangeList(a):

a[0]=10

b=[2]

ChangeList(b)

print b # ->[10]

翻译为非函数:

b=[2]

a=b #a的内存地址跟b是一样的,说明这里是内存地址拷贝,因此赋值可以理解为指针指向

a[0]=10 #将a指向的列表的第一个元素值指向内存新创建的10对象,a的内存地址没有改变,仍然跟b的内存地址一致.这里改变的只是列表内部第一个元素的地址,并未改变a的地址(也就是a指向的列表整体的地址).

print b

def ChangeList(a):

a=[10]

b=[2]

ChangeList(b)

print b # ->[2]

翻译为非函数:

b=[2]

a=b #a的内存地址跟b是一样的,说明这里是内存地址拷贝,因此赋值可以理解为指针指向

a=[10] #重新创建了一个新的列表,a指向这个新列表,a的内存地址发生了改变.

print b

结论:

总结以上3个例子,可以理解为:

1.python中的赋值"="语句,可以理解为指针的指向.

2.由于指针指向问题,故才衍生出"作用域"的概念,而不是因为有作用域的"人为限制"造成的以上结果,是吧?!

3.注意区分可变对象与不可变对象的区别.list,dict,int,char,tuple...

4.以此也推出了值传递与引用传递的概念,可以统一理解为python中都是引用传递.

说明:

int不可变的实际情况:

list可变的实际情况:

类对象与实例对象引用传递的方式:

!/bin/env python

class class_a():

num_a='abc' #当字符串足够长的时候呢?会不会是python的缓存呢?效果是一样的,不是缓存

a=class_a

b=class_a

a.num_a='xyz' #无论字符串长短,都一样

print a.num_a,b.num_a #输出xyz xyz

print id(a.num_a),id(b.num_a) #输出相同的内存地址

在这个类中,无论怎样去改变a对象,b对象都会跟着改变.

说明该对象为可变对象.类对象只是一个初始的内存空间,之后生成实例对象会把值填充进去.???

<>

知识

其实到了python中差别不大.最主要的是python是通过赋值来创建一个对象的.

所以对于python中的对象,a='1'这样不能叫修改,只能叫创建.如果你认为这样就是修改的话,就错了.

一旦使用了赋值语句,就基本上与原对象无关了.#实际上是一个指针重新的指向

而且python还有可变对象和不可变对象:

象简单类型,整数,字符串,tuple都是不可变的.

而象list, dict, object都是可变的.所以对于它们的修改一般是调用相应的方法,如对于list,你要a.append(b),对于dict你要a['b'] = '1'等.

对象本身是没有什么变化,它的属性或值发生变化.对于这种种情况,传值还是引用都是一样的.

不过,可能说引用更准确一些,比如:

def b(c):

print id(c)

a = [2]

print id(a)

b(a)

你会看到两次打印出来的id值是一样的.说明是引用传递.

也就是说函数b中的参数所指的对象就是a.

理解为传值也无所谓,因为从赋值的角度来说,在函数中的赋值会创建新的对象,不会影响到原参数.

用指针的概念去理解更合理一些吧.

其实在python中变量名与真正的对象是一种绑定关系,或引用的关系.所以理解为引用更为合适.

和其他语言不一样,传递参数的时候,python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。

python一般内部赋值变量的话,都是传个引用变量,和C语言的传地址的概念差不多。可以用id()来查询内存地址.

如果a=b的话, a和b的地址是相同的;

如果只是想拷贝,那么就得用 b=a[:],这样a和b的内存地址就不同了;

a=[1,2];b=a[:]; #这里a和b均指向一个叫做list的内存地址,list存储的,才是具体各个值对应的内存地址.列表是双层地址;

id(a)

140337215511368

id(b)

140337215510288 #不同

a='abc';b=a[:]; #这里是字符串形式,a指向的是'abc'的内存地址,b=a[:]后(相当于b=a),b同样是指向'abc'的内存地址.字符串是单层地址;这里的写法,有点多态的概念.

id(a)

140337216460600

id(b)

140337216460600 #相同

引用是指保存的值为对象的地址。在 Python 语言中,一个变量保存的值除了基本类型保存的是值外,其它都是引用,因此对于它们的使用就需要小心一些。下面举个例子:

问题描述:已知一个列表,求生成一个新的列表,列表元素是原列表的复制

a=[1,2]

b=a

这种做法其实并未真正生成一个新的列表,b指向的仍然是a所指向的对象。这样,如果对a或b的元素进行修改,a,b的值同时发生变化。

解决的方法为:

a=[1,2]

b=a[:]

这样修改a对b没有影响,修改b对a没有影响。(列表的复制,也就是副本.不是引用传递)

但这种方法只适用于简单列表,也就是列表中的元素都是基本类型,如果列表元素还存在列表的话,这种方法就不适用了。原因就是,象a[:]这种处理,只是将列表元素的值生成一个新的列表,如果列表元素也是一个列表,如:a=[1,[2]],那么这种复制对于元素[2]的处理只是复制[2]的引用,而并未生成 [2]的一个新的列表复制。

如果解决这一问题,可以使用copy模块中的deepcopy函数。修改测试如下:

import copy

a=[1,[2]]

b=copy.deepcopy(a)

print b -->[1, [2]]

a[1].append(3)

print a -->[1, [2, 3]]

print b -->[1, [2]]

有时候知道这一点是非常重要的,因为可能你的确需要一个新的列表,并且对这个新的列表进行操作,同时不想影响原来的列表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值