问题记录<1>记录一下因为忽略python按引用传递的特性而犯的错误

 第一次写blog超级紧张的O(≧口≦)O-----

 这次博客主要是想记录一下我在书写word2vec过程中对数据进行第一次压缩的过程中忽视了Python按引用传递的特性而导致的错误。

 在我首先接触的一些编程语言中,例如C/C++,里面会提到什么是按照值传递,什么是按照引用传递。我的理解是,按值传递就相当于为值单独开辟一个储存空间,当我们以按值传递的方式定义或者为一个变量赋值时,这个变量指向的是这个值的所在的储存空间,图解如下

图1 按值传递内存空间图

 在上面的图中展示了内存的分配情况。我们可以通过创建变量并同时赋值的方式实现按值传递,例如我们可以书写下面一段代码

a = constant /*此处的constant是一个常数, 可以是1,2,3等*/
C/C++为例子

 当我们书写了上面一段代码时,相当于立即开辟了两个内存,一个储存变量 a \boldsymbol{a} a另一个储存值 c o n s t a n t constant constant,并且由于施加了赋值符号,此时相当于建立了从 a \boldsymbol{a} a c o n s t a n t constant constant的指针,正如上图中的箭头一样,变量 a \boldsymbol{a} a内部保留的实际上是指针的信息。当我们调用变量 a \boldsymbol{a} a时,按值传递所做的事情实际上就是将 a \boldsymbol{a} a所指向的值 c o n s t a n t constant constant传递过去。例如,当我们利用 a \boldsymbol{a} a得到 b \boldsymbol{b} b时,可以书写下述代码

b = a/*利用变量a创建变量b*/
C/C++为例子

 上面的代码的过程实质上是这样的,当我们书写了一个赋值语句,此时在内存空间中先为变量 b \boldsymbol{b} b开辟一个空间,然后因为C/C++默认是按值传递,因此 b \boldsymbol{b} b将会访问到变量 a \boldsymbol{a} a所指向的值,然后从它自身的内存空间指向 a \boldsymbol{a} a所对应的值 c o n s t a n t constant constant所在的内存空间,正如图一中所展现的,此时内存中将会增添一个指针,因此内存空间中一共将保留两个指针。严格来说,只要是按值传递,有多少变量等于这个值就有多少指针指向这个值;(ps:因为从来没有系统地学习过C++,所以就不进行细节演示了QAQ

 那么相对应的按引用传递的含义,就是直接鲁棒地传递指针而不是传递值。具体来说,当我们创建一个变量并且为之赋值时,如果当前所要赋的值 c o n s t a n t constant constant不被任何指针所指向,也就是说此时在内存中并没有保留这个值(在Python中将会被两大机制清理掉),那么将会首先为这个值开辟一个空间,然后为新生成的变量也开辟一个空间,并且建立两者之间的指针;但是,当我们创建一个变量并且为之赋值时,如果当前所要赋的值已经在内存空间中了,那么按引用传递并不会像按值传递一样,仍然奋不顾身地再建立一个从当前变量到值 c o n s t a n t constant constant的指针,而是非常机智地获得先前就拥有的指针,并将指针的信息保留在自己的内存空间中,这就是说,当前的所有希望指向同一个值 c o n s t a n t constant constant的变量都具有同一个指针,图解如下

图2 按引用传递内存空间图


 从上图中可以看出,所有指向值 c o n s t a n t constant constant的变量都共用一个指针,这就是按引用传递;当我们给定三个赋值语句,让它们指向同一个变量时,如下

constant = 1
a = constant
b = constant
c = constant
Python为例子

 我们验证一下上述三个变量的地址(即指针)是否一致,如下
print("a的地址 :" , id(a))
print("b的地址 :" , id(b))
print("c的地址 :" , id(c))

输出结果如下

a的地址 :  9793088
b的地址 :  9793088
c的地址 :  9793088

 从上面的输出结果可以看出,实际上三个变量的地址完全相同,这就印证了三个变量共用指针的特性。共用指针可以带来一些好玩的特性,为了更加直观,我们可以借助 a \boldsymbol{a} a创建 b \boldsymbol{b} b,此时的内存分配如下

图2 借助变量创建时的内存空间图


 上图所对应的代码如下

constant = 1
a = constant
b = a

此时 b \boldsymbol{b} b保留的是 a \boldsymbol{a} a的指针信息,打印它们的地址验证如下

print("a的地址 :" , id(a))
print("b的地址 :" , id(b))

输出如下

a的地址 :  9793088
b的地址 :  9793088

 可以认为此时的 b \boldsymbol{b} b a \boldsymbol{a} a将形成一个依存关系,因为此时变量 b \boldsymbol{b} b里面其实保留着的是 a \boldsymbol{a} a的指针信息,这是否意味着,当我们手动更改了 a \boldsymbol{a} a的指针指向, b \boldsymbol{b} b也会跟着一起发生改变?在其他编程语言中的按引用传递确实如此,我们在Python中利用下面的程序尝试验证这一点

# 创设上文所讲述的环境
constant = 1
a = constant
b = a
# 打印a,b的地址
print("更改前  :")
print("a的地址 :" , id(a))
print("b的地址 :" , id(b))
# 改变a的指针
print(id(2) == id(b))#得到一个地址与b不同的数字
a = 2
# 打印a,b的地址
print("更改后  :")
print("a的地址 :" , id(a))
print("b的地址 :" , id(b))

输出结果如下

更改前  :
a的地址 : 9793088
b的地址 : 9793088
False
更改后  :
a的地址 : 9793120
b的地址 : 9793088

 可以发现 b \boldsymbol{b} b的地址居然没有发生变化,而 a \boldsymbol{a} a的指针则是已经指向了其他空间,这就有点离奇了,因为这看起来并不像前面所说的那样—— b \boldsymbol{b} b储存着的是 a \boldsymbol{a} a的指针,因此一切与 a \boldsymbol{a} a的行为同步。实际上这又涉及到Python的另一个特性,有不可变对象与可变对象一说。在此不过多解读,只不过在地址的讨论中,我们可以宣称,所有的不可变对象的指针不会因为先前公用指针的转移而发生转移,但是可变对象则会,也就是说只有可变对象才符合其他编程语言的习惯。首先列出所有的可变对象与不可变对象,然后再进行验证——

可变对象不可变对象
l i s t , s e t , d i c t list,set,dict list,set,dict i n t , f l o a t , b o o l , t u p l e , s t r i n g int,float,bool,tuple,string int,float,bool,tuple,string
可变对象和不可变对象

 我们刚才验证的显然是int类型的对象,因此会出现公用指针不会被影响的效果,所以如果我们使用的是其他的几个数据结构,又会掀起多大的波澜,我们以可变对象中的列表对象为例,验证如下
  • l i s t list list
# 创建一个列表并且依赖性赋值
l1 = [1,2,3,4]
l2 = l1
print("更改前   :")
print("l1的地址 :" , id(l1))
print("l2的地址 :" , id(l2))
#更改l1的值
l1[0] = 2
#打印二者的地址
print("更改后   :")
print("l1的地址 :" , id(l1))
print("l2的地址 :" , id(l2))

输出如下

更改前   :
l1的地址 : 139813831089280
l2的地址 : 139813831089280
更改后   :
l1的地址 : 139813831089280
l2的地址 : 139813831089280

可以看出此时的情况是,当我们更改了 l 1 l1 l1的指针后, l 2 l2 l2也随之更改,并且看上去指针所指的内存空间仍然与先前一致,实际上发生了如下的过程


图3 更改后的指针生成机制


 以上就是可变对象的指针分配规则,到此为止整个按值分配和按引用分配的讲解就到此结束了。

 感谢有人看到这里,本人小白,如有错误敬请指正!(= ̄ω ̄=)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值