【问题标题】:How does python decide whether a parameter is a reference or a value?python如何判断参数是引用还是值?
【发布时间】:2009-08-27 18:31:35
【问题描述】:

在 C++ 中,void somefunction(int) 传递一个值,而 void somefunction(int&) 传递一个引用。在 Java 中,原语是按值传递的,而对象是按引用传递的。 python是如何做出这个决定的?

编辑:既然一切都是通过引用传递的,为什么会这样:

def foo(num):
    num *= 2

a = 4
foo(a)

print(a)

打印“4”而不是“8”?

【问题讨论】:

  • 接受的答案实际上是错误的!查看 newacct 的答案,了解关于按值和按引用之间的区别的一个很好的小讨论
  • 我不同意这是错误的。讨论是关于术语的,希望我们说 python 通过引用值传递,而不是说它通过引用传递。即使从教学的角度来看,我也看不出该术语的区别。
  • 这是这个问题的副本:stackoverflow.com/questions/986006/… 遗憾的是,对于那个问题,最初接受的答案也是错误的(现在接受的答案很好),就像 Stefanos 的回答一样。
  • @nikow,是的,这是一个更好的描述。
  • 伙计们,没有所谓的“通过引用”。在 C 中,当您进行 PBR 时,这意味着您将 指针的值 复制到堆栈中,而不是实际值。您总是按值传递:您传递的值的含义(实际值或指向包含该值的位置的指针)是差异。也就是说,PBR 只是说你正在传递一些引用其他东西的实体的常规语音,因此你不能保证你传递的内容不会被被调用者改变。在这个意义上,python 是 PBR,但是你仅使用可变对象查看它

标签: python pointers reference


【解决方案1】:

它通过引用传递所有内容。即使您指定了一个数值,它也是对包含该值的表的引用。这是静态语言和动态语言之间的区别。类型与值保持一致,而不是与容器保持一致,变量只是对所有值所在的“值空间”的引用。您可以假设这个值空间包含所有可能的不可变对象(整数、浮点数、字符串)以及您创建的所有可变对象(列表、字典、对象)。当然,只有当你涉及到它们时,它们的存在才会具体化(也就是说,如果你在程序中从不使用数字 42,那么“值空间”中的值 42 就不存在分配空间)

它这样做是因为它所指的数字是一个不可变的对象。无论如何,4就是4。

def foo(num): # here, num is referring to the immutable entity 4
    num *= 2  # num now refers to the immutable entity 8

a = 4        # a now is pointing to the immutable entity 4
foo(a)       # a is still referring to the same entity 4

print(a)     # prints what a refers to, still 4

但是,如果你这样做

def foo(l):      # here, l refers to the list it receives
    l.append(5)  # the list is appended with the number 5

a = []       # a now is pointing to a specific mutable list 
foo(a)       # a is still referring to the same specific mutable list

print(a)     # prints what a refers to, the specific mutable list which now contains [5]

【讨论】:

  • 感谢您的回答。我想这类似于String 在 Java 中的工作方式。 python 中的不可变对象有哪些列表?
  • 可变的有列表、字典和其他所有内容。不可变的有字符串、元组、整数、浮点数。
  • frozenset 也是不可变的。
  • 其实是按值传递的。也就是说引用是按值传递的。
  • @StefanoBorini 正如@Michael 所指出的,它是通过“值”而不是“引用”传递的。你的解释是正确的,但我认为你应该解决这个问题。
【解决方案2】:

这里的术语存在分歧。在 Java 社区中,他们说一切都是按值传递的:原语是按值传递的;引用是按值传递的。 (如果您不相信,只需在此站点上搜索 Java 并通过引用传递。)请注意,“对象”不是语言中的值。只有对对象的引用。

它们使用的区别在于,在 Java 中,当您传递引用时,调用者范围内的原始引用变量永远不能被被调用者更改(即指向不同的对象),这应该是可能的通过引用传递。只有引用指向的对象可能会发生变异,但这无关紧要。

Python 值的工作方式与 Java 中的引用完全相同。如果我们使用相同的定义,那么我们会说 Python 中的所有内容都是引用,并且所有内容都是按值传递的。当然,Python 社区中的一些人使用不同的定义。

术语上的分歧是大部分混乱的根源。

既然你提到了 C++,那么你所拥有的 Python 代码就相当于 C++ 中的类似代码:

void foo(const int *num) {
    num = new int(*num * 2);
}

const int *a = new int(4);
foo(a);

print(a);

注意,参数是一个指针,与 Java 和 Python 中的引用最为相似。

【讨论】:

    【解决方案3】:

    响应您的编辑,这是因为整数在 Python 中是不可变的。所以a 没有改变,原因与运行此代码时没有改变的原因相同:

    a = 4
    num = a
    num *= 2
    print(a)
    

    您没有更改num(因此a),您正在创建一个新号码并将其分配给num。

    【讨论】:

      【解决方案4】:

      参数实际上是按值传递的。函数被传递给变量引用的对象,而不是变量本身。函数不能重新绑定调用者的变量。函数不能更改不可变对象,但可以更改(请求更改)可变对象。

      【讨论】:

      • 从技术上讲,它们是通过赋值传递的。所以参数被分配了参数指向的对象。这就是为什么重新分配参数不会重新分配参数的原因,因为分配会改变变量,而方法调用会改变对象。
      • @Sean,也就是所谓的“传值”
      【解决方案5】:

      一切都是通过引用传递的。一切都是对象。

      【讨论】:

      • 一切都是一个对象。
      • 其实是按值传递的。也就是说引用是按值传递的。
      • @Michael:我认为每个人都同意引用是按值传递的这一事实。你的意思是什么?
      • 我相信迈克尔的观点是,在其他语言中,“引用调用”意味着函数内部参数的更改通常在函数外部可见,这与 python 中发生的情况不完全一样。此外,python 没有变量;只有对象和名称。
      • Python 语言参考一直使用“变量”这个词。请停止拖钓。
      【解决方案6】:

      这实际上不是函数调用语义,而是赋值语义。在 Python 中,赋值是通过重新绑定引用而不是覆盖原始对象来完成的。这就是示例代码打印 4 而不是 8 的原因——它与对象的可变性无关,更重要的是 *= 运算符不是一个增变器,而是一个乘法,然后是一个赋值。这里的 num *= 2 实质上是将该函数中的“num”名称重新绑定到值“num * 2”的新对象。您传入的原始值始终保持不变。

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 2021-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      • 2016-09-16
      相关资源
      最近更新 更多