第一次写blog超级紧张的O(≧口≦)O-----
这次博客主要是想记录一下我在书写word2vec过程中对数据进行第一次压缩的过程中忽视了Python按引用传递的特性而导致的错误。
在我首先接触的一些编程语言中,例如C/C++,里面会提到什么是按照值传递,什么是按照引用传递。我的理解是,按值传递就相当于为值单独开辟一个储存空间,当我们以按值传递的方式定义或者为一个变量赋值时,这个变量指向的是这个值的所在的储存空间,图解如下
![](https://img-blog.csdnimg.cn/img_convert/a3640b85e615b4741ec8ecd306342dae.png)
a = constant /*此处的constant是一个常数, 可以是1,2,3等*/
当我们书写了上面一段代码时,相当于立即开辟了两个内存,一个储存变量 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*/
上面的代码的过程实质上是这样的,当我们书写了一个赋值语句,此时在内存空间中先为变量
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的变量都具有同一个指针,图解如下
![](https://img-blog.csdnimg.cn/img_convert/ed4c696ce2e2395ed9cd16f326353b64.png)
从上图中可以看出,所有指向值 c o n s t a n t constant constant的变量都共用一个指针,这就是按引用传递;当我们给定三个赋值语句,让它们指向同一个变量时,如下
constant = 1
a = constant
b = constant
c = constant
我们验证一下上述三个变量的地址(即指针)是否一致,如下
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,此时的内存分配如下
![](https://img-blog.csdnimg.cn/img_convert/da3080c16e60f04689a07374d97b1c6e.png)
上图所对应的代码如下
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也随之更改,并且看上去指针所指的内存空间仍然与先前一致,实际上发生了如下的过程
![](https://img-blog.csdnimg.cn/img_convert/0f90e1922367ef9ec2376a9e51cd91b7.png)
以上就是可变对象的指针分配规则,到此为止整个按值分配和按引用分配的讲解就到此结束了。
感谢有人看到这里,本人小白,如有错误敬请指正!(= ̄ω ̄=)