让我们从基础开始。你可能已经知道这一点,或者至少认为你知道。令人惊讶的是,许多人实际上相当多地滥用了这些术语(这当然无助于混淆)。
什么是传值和传引用?
我现在不打算告诉你答案,而是用例子来解释它。稍后你会明白为什么。
假设您正在尝试学习一门编程语言,而一位朋友告诉您他知道一本关于该主题的好书。你有两个选择:
- 向他借书或
- 购买您自己的副本。
案例 1:如果您借他的书并在页边空白处做笔记(并添加一些书签、参考资料、图纸……),您的朋友以后也可以从这些笔记和书签中受益。分享是关怀。世界真好!
案例 2:但是,另一方面,如果您的朋友有点爱整洁,或者非常珍惜他的书,他可能不会欣赏您的笔记,更不用说偶尔的咖啡渍或您如何折叠角落以添加书签.你真的应该得到你自己的书,每个人都可以愉快地以他喜欢的方式对待他的书。世界真好!
这两种情况——你猜对了——就像传递引用和传递值一样。
区别在于函数的调用者是否可以看到被调用者所做的更改。
让我们看一个代码示例。
假设你有以下代码 sn -p:
function largePrint(text):
text = makeUpperCase(text)
print(text)
myText = "Hello"
largePrint(myText)
print(myText)
然后你用传值语言运行它,输出将是
HELLO
Hello
在传递引用的语言中,输出将是
HELLO
HELLO
如果你通过引用传递变量“myText”被函数改变。如果您只是传递变量的值,则函数无法更改它。
如果你需要另一个例子,this 一个使用网站的人得到了很多支持。
好的,现在我们已经掌握了基础知识,让我们继续...
什么是按值传递引用?
让我们再次使用我们的书籍示例。假设您决定向您的朋友借这本书,尽管您知道他是多么喜欢他的书。让我们也假设你当时——作为一个完全的笨蛋——把一加仑咖啡洒在上面。天啊...
不用担心,你的大脑会启动并意识到你可以给你的朋友买一本新书。它的状况如此之好,他甚至都不会注意到!
这个故事与按值传递引用有什么关系?
好吧,另一方面,想象一下你的朋友非常喜欢他的书,他不想借给你。然而,他确实同意把这本书带来给你看。问题是,你仍然可以将一加仑咖啡洒在上面,但现在你不能再给他买一本新书了。他会注意到的!
虽然这听起来很可怕(并且可能让你像喝了那加仑咖啡一样出汗),但它实际上很常见,并且是 share book 调用函数的绝佳方式。它有时被称为按值传递引用。
代码示例的表现如何?
在按值传递引用的函数中,我们的 sn-p 的输出是
HELLO
Hello
就像在按值传递的情况下一样。但是如果我们稍微修改一下代码:
function largePrint(text):
text.toUpperCase()
print(text)
myText = "Hello"
largePrint(myText)
print(myText)
输出变成:
HELLO
HELLO
就像在传递引用的情况下一样。原因与书籍示例中的相同:我们可以通过调用 text.toUpperCase() (或将咖啡洒在上面)来更改变量(或书籍)。但是我们不能再改变变量引用的对象(text = makeUppercase(text) 没有效果);换句话说,我们不能使用不同的书。
总结一下,我们得到:
按值传递:
你得到了你自己的书,你自己的变量副本,以及你用它做的任何事情,原始所有者永远不会知道。
参考传递:
您使用与您的朋友相同的书或与调用者相同的变量。如果您更改变量,它会在您的函数之外更改。
按值传递引用
你和你的朋友使用的是同一本书,但他并没有把它从他的视线中移开。你可以换书,但不能换书。在变量方面,这意味着您不能更改 what 变量引用,但您可以更改 which 变量引用。
现在,当我向您展示以下 Python 代码时:
def appendSeven(list):
list.append(7)
def replaceWithNewList(list):
list = ["a", "b", "c"]
firstList = [1, 2, 3, 4, 5, 6]
print(firstList)
appendSeven(firstList)
print(firstList)
secondList = ["x", "y", "z"]
print(secondList)
replaceWithNewList(secondList)
print(secondList)
并告诉你输出是这样的
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
['x', 'y', 'z']
['x', 'y', 'z']
你说什么?
确实,你是对的! Python 使用按值传递引用! (Java、Ruby 和 Scheme 也是如此……)
结论:
这非常困难吗?不。
那为什么那么多人会被它迷惑呢? (我不会链接到有关“如何在 Python 中通过引用传递变量”或“Java 是否通过引用传递?”等所有其他问题......)
嗯,我看到了几个原因:
- 说按引用传递值真的很长(无论如何它的行为与按引用传递几乎相同,所以请放下我的背,停止分裂!)
- 有一些令人讨厌的特殊情况使问题复杂化
- 类似于原始类型(在按引用传递值的语言中通常是按值传递)
- 不可变类型(无法更改,因此通过引用传递它们不会让您到达任何地方)
- 有些人只是喜欢使用不同的名称:
-
pass-by-copy 用于 pass-by-value,因为如果您注意,您需要自己的书,因此必须制作副本。 (但这是一个实现细节,其他技巧(如写时复制)可能仅用于制作副本。)
-
pass-by-reference 用于 pass-reference-by-value 因为毕竟您确实获得了引用,这意味着您可以更改调用者拥有的对象(即使你不能交换它)
-
pass-by-reference 用于 pass-reference-by-value,因为几乎没有语言实际上具有 pass-by-reference(值得注意的例外(我知道):C++、C#但是你必须使用特殊的语法)
-
pass-by-sharing 用于 pass-reference-by-value(还记得我们书中的例子吗?这个名字实际上更好,每个人都应该使用它!)
-
pass-by-object 用于 pass-reference-by-value,因为据称 Barbara Liskov 首先命名它并使用它,所以这是它的出生名)
-
pass-by-pointer 用于 pass-by-reference,因为 C 没有 pass-by-reference,这就是它允许您完成相同事情的方式。
如果您认为我应该添加任何内容,请告诉我。如果我犯了错误,请告诉我!
对于那些还没有足够的人:http://en.wikipedia.org/wiki/Evaluation_strategy