在 Python 中传递值 [duplicate]

Passing values in Python [duplicate]

提问人:Joan Venge 提问时间:2/11/2009 更新时间:9/7/2023 访问量:95573

问:

当您将 list 等集合、数组传递给 python 中的另一个函数时,它是复制它,还是只是一个指针?

Python 按引用传递

评论

18赞 Noob Saibot 11/30/2013
这个问题是在 09 年 2 月提出的,而“原始”问题是在当年 6 月提出的。为什么这个是重复的?
5赞 Makoto 12/29/2013
@NoobSaibot:“副本”中的答案要好得多
1赞 akki 3/5/2017
Mark Ransom 的这个答案effbot 关于 Python 对象的这篇博客一起将使事情尽可能清晰。
2赞 cellepo 9/29/2020
@Makoto这并不能证明第一个问题被标记为重复是合理的。这鼓励了重复的答案。
1赞 Joan Venge 10/1/2020
@cellepo:我的评论也被删除了。审查制度太好了,工作模组!

答:

4赞 S.Lott 2/11/2009 #1

对象被传递。不是副本,而是对基础对象的引用。

评论

0赞 Tim Richardson 11/11/2015
但是,您还需要知道 Python 何时创建新对象,以及何时取消引用。
102赞 Stephen 2/11/2009 #2

Python 按值传递对对象的引用

Python 通过以下方式传递对对象的引用 value(如 Java)以及 Python 是一个对象。这听起来 很简单,但随后你会注意到 某些数据类型似乎表现出 按值传递特征,而 其他人似乎表现得像 通过引用...这是怎么回事?

了解可变性很重要 和不可变对象。一些对象, 与字符串、元组和数字一样,是 变。在 函数/方法将创建一个新的 实例和原始实例 函数/方法之外不是 改变。其他对象,如列表 和字典是可变的,这 表示您可以更改对象 就地。因此,将 函数/方法中的对象将 同时更改原始对象 外面。

评论

19赞 Daniel Pryden 1/13/2012
你首先说“Python 通过引用传递”,但在引用的文本中,它说“Python 通过值传递对对象的引用”——这不是一回事!Python 实际上根本不是引用调用,而是共享调用
14赞 user4815162342 11/28/2012
Python 不通过引用传递,因为该术语通常被理解(例如 Pascal 或 C++)。分配给变量不会以任何方式影响调用方,无论涉及何种数据类型,情况都是如此。调用函数时,Python 会为同一对象创建一个新名称,以便将对象的变异反映在调用方中,但分配给函数局部变量则不会。这与 Java 或 Lisp 中的机制完全相同。不幸的是,你的回答增加了混乱。
0赞 vipulnj 7/25/2017
@user4815162342:为了清楚起见,分配给函数中的局部变量将创建一个新对象,该对象在函数执行期间将处于活动状态,并在函数退出时死亡。正确?
0赞 user4815162342 7/25/2017
@vipulnj 正确。当然,除非对象存储在全局变量中,在这种情况下,它将在函数退出后继续存在 - 但它仍然不会影响调用者看到的对象。
9赞 anthony 2/11/2009 #3

传递引用,但如果参数是不可变对象,则在方法中修改它将创建一个新实例。

3赞 Harper Shelby 2/11/2009 #4

通过引用:

>>> x = [0,1,2,3]
>>> def foo(x_list):
    x_list[0] = 1


>>> foo(x)
>>> x
[1, 1, 2, 3]

评论

0赞 S.Lott 2/11/2009
@Harper Shelby:很好,但有风险的例子。不适用于不可变对象,如字符串、元组、整数等。
0赞 Josh Heitzman 11/15/2012
没错,但这并不是因为不可变对象是按值传递的,而是因为它们在 mutate 时自动复制。
3赞 Matt 2/11/2009 #5

我还建议查看该模块:copy

用于复制的 Python 文档

它将帮助您了解潜在问题以及如何使用它来执行自己的深度复制。

84赞 nosklo 2/11/2009 #6

问题是,整个引用/值概念不适合 python。Python 没有变量的“值”。Python 只有对象和引用对象的名称。

因此,当您调用一个函数并在括号内放置一个“名称”时,如下所示:

def func(x): # defines a function that takes an argument
    ... # do something here

func(myname) # calling the function

传递的是指向的实际对象,而不是名称本身。在函数中,给出了另一个名称 () 来引用传递的同一对象。mynamemynamex

如果函数内部的对象是可变的,则可以修改该对象,但不能更改外部名称所指向的内容。当你这样做时也会发生同样的情况

anothername = myname

因此,我可以用以下方式回答您的问题:

它是“按值传递”,但所有值都只是对对象的引用。

评论

0赞 Tim Richardson 11/11/2015
我决定下次被要求解释这一点时,我将使用 id() 函数来显示名称与引用的绑定。
1赞 Elazar 9/5/2016
名称是当前作用域中的引用。“names”和“variables”(如在Java中)之间唯一明显的区别是当你看的时候,大多数时候你不会看。是的,Java 有几种类型的原语,而 Python 只有一种。locals()
1赞 Tomerikoo 8/18/2020
最后一句话完美地总结了我看到许多 Python 新手感到非常困惑的事情!
1赞 Antonio Leite 10/22/2012 #7

请让我举一个谦虚的例子

def swap(a, b):
    x = a
    print id(x)
    print id(a)
    print id(b)
    a = b
    
    print id(a)
    b = x
    print id(b)
    a[0]= '20'
    



var1 = ['1','2','3','4']
var2 = ['5','6','7','8','9']
print id(var1)
print id(var2)

swap(var1, var2)

print id(var1)
print id(var2)
print var1
print var2

这将产生以下结果

28329344 var1
28331264 var2
28329344 x
28329344 a
28331264 b
After a = b
28331264 a
after b = x
28329344 b
after return
28329344 var1
28331264 var2
['1', '2', '3', '4']
['20', '6', '7', '8', '9']

Mapping to the memory addresses
28329344                 28331264 
var1                     var2
a                        b
x
After a=b
                         a
After b=x
b
After a[0] = '20'
                         [0] = '20'
After return
['1','2','3','4']        ['20', '6', '7', '8', '9']
28赞 cellepo 11/27/2012 #8

这里的答案很有帮助,但我发现有必要展示这种我没有看到的细微区别,我已经在随后的 CL 实验中向自己证明了这一点:

  1. 不能在函数调用中单独更改不可变对象。(到目前为止,答案已经说了这么多......
  2. 但是,包含在可变对象中的不可变对象可以在方法调用中重新赋值。

'num' 在这里没有改变,因为它是一个不可变的 Number 对象 [支持我的观点 1.]:

>>> def incr_num(num):
        num += 1

>>> num = 0

>>> num
0

>>> incr_num(num)

>>> num
0

list[0]这里也是一个不可变的 Number 对象。

>>> def incr_list(list):
        list[0] += 1

>>> list = [0]

>>> list[0]
0

>>> incr_list(list)

>>> list[0]
1

那么,作为一个不可变的 Number 对象,list[0] 是如何改变的(支持我的观点 2.),而上面的例子的 Number 对象 'num' 没有呢?不可变的 Number 对象包含在变列表对象“list”中,而第一个示例中的“num”只是一个不包含的 Number 对象(不可变)。list[0]

虽然用心良苦,但我觉得@Stephen Pape 最受好评的答案(引用如下)和其他一些类似的答案并不完全正确(这促使我写了这个答案):

某些对象(如字符串、元组和数字)是不可变的。 在函数/方法中更改它们将创建一个新实例和 函数/方法外部的原始实例不会更改。

我上面的第二个代码实验显示了一个 Number 对象 ('list[0]') 在方法内被更改,然后函数外部的原始实例发生了变化。

评论

1赞 sancho.s ReinstateMonicaCellio 7/16/2015
这是早期答案 stackoverflow.com/a/534389/2707864 的较长版本,值得发布。
0赞 IqbalHamid 6/30/2021
@cellepo 您的回答解释了这种行为,但没有解释这种行为的原因。我的意思是,当作为参数传递时,列表中的数字有什么区别;以及直接作为参数传递的整数。为什么一个整数会改变而另一个整数不会改变?
0赞 cellepo 7/1/2021
@IqbalHamid为什么你不像你对这里的另一个答案的评论那样善待或尊重我呢?你为什么不也问他们你的问题?我什至提供了比他们更多的解释,为此你要求我提供更多解释......
0赞 IqbalHamid 7/2/2021
@celleppo。没有不尊重的意思。你提出了一个引人入胜的观察结果。这是 python 程序员需要注意的。因此,感谢您提请我们注意这一点。但是我最初阅读了您的答案,这让我不清楚为什么这种行为会在 python 中发生。您继续强调了这两种情况之间的区别。但是我无法理解使它可变的列表是什么。其他人的代码与您的代码相同,但返回引用可以更清楚地了解正在发生的事情以及如何发生。没有不尊重的意思。你的回答仍然有帮助
0赞 cellepo 7/3/2021
我从未声称要解释任何适合你个人的推理;问题没有提出要求。但我的自由答案确实已经对推理进行了解释——这是我的答案顶部已经存在的粗体字——你可以在编辑历史中看到,该部分自您第一次评论之前以来没有变化,这表明您缺乏尽职调查: 更仔细地阅读,并研究自己,根据您的个人需求偏好找到自己的答案 - 至少尝试一下, 在批评我的免费建议之前(它已经达到了您的要求)。