最近在学习C,打印问题时遇到了有关引用、指针的问题。 重申相关知识后,发现C中的引用与python中的引用有很大的不同。 我认为这是C比python效率高得多的原因之一。 在两篇文章中,我想整理python和c在引用上的差异,以及c在引用和指针上的差异。
引用Python
python中的引用是引用赋值,与浅副本相同。 举个例子:
In [13]: a=1
In [14]: b=a
in[15]:id(a ) )
Out[15]: 4553065616
in[16]:id(b ) )
Out[16]: 4553065616
In [20]: b is a
out [ 20 ] :真
In [21]: b==a
out [ 21 ] :真
在上面的代码中,不需要声明变量类型,因此首先初始化了值为1且名称为a的变量。 实际上,python没有变量。 正确的是标签。 然后,变量b引用标签a。 此时,标签b的值等于标签a的值,存储器地址也相同。
但是,在python中引用还有另一个特性。 可以更改APP应用程序
In [23]: b=2
in[24]:id(b ) )
Out[24]: 4553065648
In [25]: a
Out[25]: 1
in[26]:id(a ) )
Out[26]: 4553065616
----------------
In [24]: b=b 1
In [25]: b
Out[25]: 2
In [26]: a
Out[26]: 1
in[27]:id(a ) )
Out[27]: 4369581200
in[28]:id(b ) )
Out[28]: 4369581232
此时,变量b不引用变量a,而是引用名为2的int数据,存储器地址也不再与a相同。 回到a一看,发现a没有受到b的影响。 更详细地说,这是因为在python中,int数据类型是不可变的数据类型,因此在变量b被引用后没有更改a的值或内存,也就是说int是线程安全的数据类型。
销售点
python引用与c的最大区别在于,c的引用一经赋值就不能更改引用对象,后续赋值操作只能修改引用对象本身。
另一方面,python不一样。 没有这个概念,代入引用后,会将标签附加到最新的对象上。
可变类型数据
接下来,让我们看一下python中对可变类型数据的引用
in [1] : a=[ 1,2,3 ]
In [2]: b=a
in[4]:b.append(4) )。
In [5]: b
out [5] : [ 1,2,3,4 ]
in[6]:id(b ) )
Out[6]: 4415640192
in[7]:id(a ) )
Out[7]: 4415640192
In [8]: a
out [8] : [ 1,2,3,4 ]
对于可变类型数据,标签b首先参考标签a引用的对象。 此时,如果操作标签b更改数据,则可以看到标签a所参照的对象也发生了更改。
对可变类型的数据执行变更操作时,该操作本身将成为参考对象。 因此,无论a还是b进行更改操作,都会影响该对象的其他引用。
当对可变类型数据进行重新赋值操作时
in [ 12 ] : a=[ 1,2,3 ]
In [13]: b=a
in [ 14 ] : b=[ 2,3,4 ]
In [15]: a
out [ 15 ] : [ 1,2,3 ]
in[16]:id(a ) )
Out[16]: 4406584576
in[17]:id(b ) )
Out[17]: 4406530608
这时又回到了和不变类型一样的情况,标签b去重新引用新的对象,和a说了拜拜。
让我们看看更典型的例子
发生这种情况是因为更改可变数据的操作实际上是更改引用
in [ 37 ] 3360 a=[ 1,2,3 ]
In [38]: a[1]=a
In [39]: a
Out[39]: [1,[.],3]
我进行了变更操作,使a[1]指向标签a所参照的对象。 原以为结果为[1,[1,2,3],3 ],但变更操作只会修改参照对象本身,列表[ 1, 3 )的? A指着自己引起了循环
为了避免这种情况,可以通过复印解决这个问题。
in [ 41 ] : a=[ 1,2,3 ]
In [42]: a[1]=copy.copy(a (
In [43]: a
Out[43]: [1,[ 1,2,3],3 ]
浅拷贝
对于不可变类型,写副本与赋值相同
In [49]: a=1
in[50]:b=copy.copy(a ) )
In [51]: b
Out[51]: 1
in[52]3360id(a ) )
Out[52]: 4369581200
in[53]3360id(b ) )。
Out[53]: 4369581200
标签b和标签a都指的是这个数据。
看看导致上面循环的问题吧。 在这里可以用复印解决。
in [1] : a=[ 1,2,3 ]
In [2]: a[1]=a[:]
In [3]: a
Out[3]: [1,[ 1,2,3],3 ]
在本例中,您将使用切片作为复制操作,并将a引用的对象的第二个元素指向复制的新对象。
浅析复印缺陷
有一天,不小心发现了薄薄的复印的缺点,很有趣。
In [31]: a=[1,[ 2,3 ],4]
In [32]: b=a[:]
in[33]3360id(a[1] )
Out[33]: 4436979376
in[34]:id(b[1] )
Out[34]: 4436979376
In [40]: a[1][1]=2
In [41]: a
Out[41]: [1,[ 2,2 ],4]
In [42]: b
Out[42]: [1,[ 2,2 ],4]
首先,如果浅复制可变数据,编译器将总结为保存新对象而重新打开内存。 也就是说,复制了可变数据的新对象与原始对象不同。 但是,在上述示例中,如果不变数据作为不变数据对象的元素存在,则副本将其作为不变对象处理,因此可以看出上述a[1]和b[1]的存储器地址相同。
解决这个问题必须使用深度拷贝
深度复制
In [43]: a=[1,[ 2,3 ],4]
in[44]:b=copy.deepcopy(a ) )
In [45]: a[1][1]=2
In [46]: a
Out[46]: [1,[ 2,2 ],4]
In [47]: b
Out[47]: [1,[ 2,3 ],4]
为通过深度复制嵌套的元素创建新的内存。