Python基础系列之--变量、赋值、引用【7】

目标

  1. 了解变量名与变量的关系
  2. 理解引用、拷贝、赋值等概念与含义

1 前提概况

我们接触最多的就是变量,究竟变量名变量值 关系是啥?如何存储?

首先看一个最简单的

a = 100

上侧是一个赋值操作,其中a是变量名100是变量的值。编程语言的编译器(或者解释器)处理到这一句代码的时候,一般语言会这样做

  1. 在内存中开辟一个内存空间, 地址0x4377878280;
  2. 存放100进第一步的空间中;
  3. 取值100时,找到地址为0x4377878280即可。

但是
但是
但是

这么长除了计算机能记住,我是记不住。正如ip地址与域名的关系,我们只需要记住taobao.com即可,所以,编译器给我们做了个表,在这个表中,它将内存地址和变量名做了个映射

变量名 内存地址
a 0x4377878280
b 0x4377878281
c 0x4377878282
….

变量名完全可以看成一个内存地址的别名(只是方便我们记忆),真正的数据是存在这个内存地址的存储空间上的。变量名在运行的时候,没有任何用处。

赋值

2. 深入一些

奔着:以问题来解决疑问题,先看这个代码,思考一下结果

>>> values = [0, 1, 2]
>>> values[1] = values

请认真思考此时 values 是多少?
请认真思考此时 values 是多少?
请认真思考此时 values 是多少?

也许你认为是[0, [0, 1, 2], 2],原因可能是:

  1. list 可变;
  2. 把list[1]的位置直接添加一个list;

代码如下,完全符合预期.

>>> a = [1, 2, 3]
>>> a[1] = [1, 2, 3]
>>> a
[1, [1, 2, 3], 3]

但是结果却是 无限循环

>>> values
[0, [...], 2]

❌就❎在第二步中
其中涉及两个概念:引用、拷贝

第一部分已经说过,变量名是内存的别称

a=10的含义:内存中有个地址0x1234567的空间存了10,然后a指向这块内存。

变量多次'赋值‘

上图中很好的证明了,变量‘赋不同值’,只是引用不同而已

values[1] = values只是把自身指向自身。如图:

自身引用自身

如需达到预期效果,只需要类似于你的逻辑,指向一份值相同但内存不同的值

值相同但内存不同

代码如下:

# 拷贝生成一份新的数据,
>>> values[1] = values[:]
>>> values
[0, [0, 1, 2], 2]

往更深处说,values[:] 复制操作是所谓的「浅复制」(shallow copy),当列表对象有嵌套的时候也会产生出乎意料的错误,比如

a = [0, [1, 2], 3]
b = a[:]
a[0] = 8
a[1][1] = 9
# 正确答案
>>> print(a)
[8, [1, 9], 3]
>>> print(b)
[0, [1, 9], 3]
# a 与 b 的地址确实不同
>>> print(id(a))
4355761096
>>> print(id(b))
4355761416
# 但是内部list的地址确实相同的,也就是共同指向了同一块内存地址
>>> print(id(a[1]))
4354260680
>>> print(id(b[1]))
4354260680

看完上图的打印结果,请看下图;

浅复制

重点: values[:] 只是浅层复制
重点: values[:] 只是浅层复制
重点: values[:] 只是浅层复制

正确的复制嵌套元素的方法是进行「深复制」(deep copy),方法是

import copy

a = [0, [1, 2], 3]
b = copy.deepcopy(a)
a[0] = 8
a[1][1] = 9

深复制

3 引用 VS 拷贝:

  1. 没有限制条件的分片表达式(L[:])能够复制序列,但此法只能浅层复制
  2. 字典 copy 方法,D.copy() 能够复制字典,但此法只能浅层复制
  3. 有些内置函数,例如 list,能够生成拷贝 list(L)
  4. copy 标准库模块能够生成完整拷贝:deepcopy 本质上是递归 copy

参考链接

python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域 原