返回介绍

4.7 引用

发布于 2024-01-22 21:44:07 字数 3261 浏览 0 评论 0 收藏 0

正如你看到的,变量保存字符串和整数值。在交互式环境中输入以下代码:

>>> 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文