- 内容提要
- 作者简介
- 技术评审者简介
- 致谢
- 译者序 会编程的人不一样
- 前言
- 本书的读者对象
- 编码规范
- 什么是编程
- 本书简介
- 下载和安装 Python
- 启动 IDLE
- 如何寻求帮助
- 聪明地提出编程问题
- 小结
- 第一部分 Python 编程基础
- 第1章 Python 基础
- 第2章 控制流
- 第3章 函数
- 第4章 列表
- 第5章 字典和结构化数据
- 第6章 字符串操作
- 第二部分 自动化任务
- 第7章 模式匹配与正则表达式
- 第8章 读写文件
- 第9章 组织文件
- 第10章 调试
- 第11章 从 Web 抓取信息
- 第12章 处理 Excel 电子表格
- 第13章 处理 PDF 和 Word 文档
- 第14章 处理 CSV 文件和 JSON 数据
- 第15章 保持时间、计划任务和启动程序
- 第16章 发送电子邮件和短信
- 第17章 操作图像
- 第18章 用 GUI 自动化控制键盘和鼠标
- 附录A 安装第三方模块
- 附录B 运行程序
- 附录C 习题答案
4.7 引用
正如你看到的,变量保存字符串和整数值。在交互式环境中输入以下代码:
>>> spam = 42 >>> cheese = spam >>> spam = 100 >>> spam 100 >>> cheese 42
你将42赋给spam变量,然后拷贝spam中的值,将它赋给变量cheese。当稍后将spam中的值改变为100时,这不会影响cheese中的值。这是因为spam和cheese是不同的变量,保存了不同的值。
但列表不是这样的。当你将列表赋给一个变量时,实际上是将列表的“引用”赋给了该变量。引用是一个值,指向某些数据。列表引用是指向一个列表的值。这里有一些代码,让这个概念更容易理解。在交互式环境中输入以下代码:
❶ >>> spam = [0, 1, 2, 3, 4, 5] ❷ >>> cheese = spam ❸ >>> cheese[1] = 'Hello!' >>> spam [0, 'Hello!', 2, 3, 4, 5] >>> cheese [0, 'Hello!', 2, 3, 4, 5]
这可能让你感到奇怪。代码只改变了cheese列表,但似乎cheese和spam列表同时发生了改变。
当创建列表时❶,你将对它的引用赋给了变量。但下一行❷只是将spam中的列表引用拷贝到cheese,而不是列表值本身。这意味着存储在spam和cheese中的值,现在指向了同一个列表。底下只有一个列表,因为列表本身实际从未复制。所以当你修改cheese变量的第一个元素时❸,也修改了spam指向的同一个列表。
记住,变量就像包含着值的盒子。本章前面的图显示列表在盒子中,这并不准确,因为列表变量实际上没有包含列表,而是包含了对列表的“引用”(这些引用包含一些ID数字,Python在内部使用这些ID,但是你可以忽略)。利用盒子作为变量的隐喻,图4-4展示了列表被赋给spam变量时发生的情形。
图4-4 spam = [0, 1, 2, 3, 4, 5]保存了对列表的引用,而非实际列表
然后,在图4-5中,spam中的引用被复制给cheese。只有新的引用被创建并保存在cheese中,而非新的列表。请注意,两个引用都指向同一个列表。
图4-5 spam = cheese复制了引用,而非列表
当你改变cheese指向的列表时,spam指向的列表也发生了改变,因为cheese和spam都指向同一个列表,如图4-6所示。
图4-6 cheese[1] = 'Hello!'修改了两个变量指向的列表
变量包含对列表值的引用,而不是列表值本身。但对于字符串和整数值,变量就包含了字符串或整数值。在变量必须保存可变数据类型的值时,例如列表或字典,Python就使用引用。对于不可变的数据类型的值,例如字符串、整型或元组,Python变量就保存值本身。
虽然Python变量在技术上包含了对列表或字典值的引用,但人们通常随意地说,该变量包含了列表或字典。
4.7.1 传递引用
要理解参数如何传递给函数,引用就特别重要。当函数被调用时,参数的值被复制给变元。对于列表(以及字典,我将在下一章中讨论),这意味着变元得到的是引用的拷贝。要看看这导致的后果,请打开一个新的文件编辑器窗口,输入以下代码,并保存为passingReference.py:
def eggs(someParameter): someParameter.append('Hello') spam = [1, 2, 3] eggs(spam) print(spam)
请注意,当eggs()被调用时,没有使用返回值来为spam赋新值。相反,它直接当场修改了该列表。在运行时,该程序产生输出如下:
[1, 2, 3, 'Hello']
尽管spam和someParameter包含了不同的引用,但它们都指向相同的列表。这就是为什么函数内的append('Hello')方法调用在函数调用返回后,仍然会对该列表产生影响。
请记住这种行为:如果忘了Python处理列表和字典变量时采用这种方式,可能会导致令人困惑的缺陷。
4.7.2 copy模块的copy()和deepcopy()函数
在处理列表和字典时,尽管传递引用常常是最方便的方法,但如果函数修改了传入的列表或字典,你可能不希望这些变动影响原来的列表或字典。要做到这一点,Python提供了名为copy的模块,其中包含copy()和deepcopy()函数。第一个函数copy.copy(),可以用来复制列表或字典这样的可变值,而不只是复制引用。在交互式环境中输入以下代码:
>>> import copy >>> spam = ['A', 'B', 'C', 'D'] >>> cheese = copy.copy(spam) >>> cheese[1] = 42 >>> spam ['A', 'B', 'C', 'D'] >>> cheese ['A', 42, 'C', 'D']
现在spam和cheese变量指向独立的列表,这就是为什么当你将42赋给下标7时,只有cheese中的列表被改变。在图4-7中可以看到,两个变量的引用ID数字不再一样,因为它们指向了独立的列表。
图4-7 cheese = copy.copy(spam)创建了第二个列表,能独立于第一个列表修改
如果要复制的列表中包含了列表,那就使用copy.deepcopy()函数来代替。deepcopy()函数将同时复制它们内部的列表。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论