Python引用(指针)

其实文章的标题严谨性有所欠缺,虽然底层是用C语言实现的,但是python语言没有指针这一说。这么取名是为了和我们学过的内容相互联系类比,从而获得更好的理解。

在Python中,有时候想当然认为使用等号赋值如

b = [1, 2, 3, 4]
a = b

可以获得一个变量的副本,其实并不如此。实际上此时的a获得的是b的一个引用,如下图所示:
可以看

可以看到,a和b指向的是同一个内容,也即此时的等号赋值并不是复制,而是传递引用。两者指向同一个对象。

这个问题在很多场合都没有问题(例如遍历a和遍历b),只是相当于换了一个名字而已,用a和b都没有任何区别。

但是当到了需要对数据操作的时候(例如增加数据,删除数据)的时候就会发生问题。

给定如下程序,试分析其输出:

a = {'a': 1}
c = []
for i in range(3):
    b = a
    b['b'] = i
    c.append(b)
print(c)

第一直觉分析,输出列表c包含3个字典,每个字典包含2个项,如下所示:

[{'a': 1, 'b': 0}, {'a': 1, 'b': 1}, {'a': 1, 'b': 2}]

然而实际输出为:

[{'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]

为什么其中的每个项都是一样的?

错误在于使用了b = a对a进行复制,该语句并不会对a进行复制,而是生成一个a的引用。

在这里插入图片描述

如上图所示,每次使用b = a时,都只是生成了字典a的引用,每次append到列表c中的都是字典a的引用,所以每次修改a都对列表c中的所有项生效,经过3次循环,最后一次修改字典a得到的是{'a': 1, 'b': 2},于是列表c中的每一项都是一样的。

可以在以上程序再做进一步修改:

a = {'a': 1}
c = []
for i in range(3):
    b = a
    b['b'] = i
    c.append(b)
a['a'] = 666
a['c'] = 888
print(c)

输出为:

[{'a': 666, 'b': 2, 'c': 888}, {'a': 666, 'b': 2, 'c': 888}, {'a': 666, 'b': 2, 'c': 888}]

可以看到每次对a的修改都会作用到列表c中的每个元素,因为列表c中存储的并不是具体的值,而是一个引用(指针),他们都指向同一个位置,牵一发而动全身。

如何修改才能正常?

经过上面分析,只需要每次append到列表c中的不是同一个内容即可,我们使用copy方法获取a的副本,从而每次append到列表c中的内容都是一个新的值,如下所示:

a = {'a': 1}
c = []
for i in range(3):
    b = a.copy()
    b['b'] = i
    c.append(b)
print(c)

输出结果:

[{'a': 1, 'b': 0}, {'a': 1, 'b': 1}, {'a': 1, 'b': 2}]

可以看到这一次的结果如同预期。

动态演示上面两个代码的区别:

  1. 未添加copy方法

在这里插入图片描述

  1. 添加copy方法

在这里插入图片描述
文章中可视化执行python代码网站地址:链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值