参数是通过引用传递还是通过值传递?如何通过引用传递,以便下面的代码输出“Changed”而不是“Original”?

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

另请参阅:为什么函数可以修改调用者感知的某些参数,而不能修改其他参数?


当前回答

正如你所说的,你需要一个可变的对象,但我建议你检查全局变量,因为它们可以帮助你甚至解决这类问题!

http://docs.python.org/3/faq/programming.html#what-是python中局部和全局变量的规则

例子:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

其他回答

我通常使用的一个简单技巧是将其包装在列表中:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(是的,我知道这可能很不方便,但有时这样做很简单。)

正如你所说的,你需要一个可变的对象,但我建议你检查全局变量,因为它们可以帮助你甚至解决这类问题!

http://docs.python.org/3/faq/programming.html#what-是python中局部和全局变量的规则

例子:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

(编辑-布莱尔更新了他广受欢迎的答案,使其准确无误)

我认为重要的是要注意到,目前获得最多选票的帖子(布莱尔•康拉德),虽然其结果正确,但却具有误导性,并且根据其定义,几乎不正确。虽然有许多语言(如C)允许用户通过引用传递或通过值传递,但Python不是其中之一。

大卫·库尔纳波的回答指向了真正的答案,并解释了为什么布莱尔·康拉德帖子中的行为似乎是正确的,而定义却不正确。

在Python是按值传递的情况下,所有语言都按值传递,因为必须发送一些数据(无论是“值”还是“引用”)。然而,这并不意味着Python是按C程序员会想到的值传递的。

如果你想要这种行为,Blair Conrad的回答很好。但如果你想知道Python既不是通过值传递,也不是通过引用传递的根本原因,请阅读大卫·库尔纳波的答案。

Effbot(又名Fredrik Lundh)将Python的变量传递风格描述为对象调用:http://effbot.org/zone/call-by-object.htm

对象在堆上分配,指向它们的指针可以在任何地方传递。

当您进行赋值(如x=1000)时,将创建一个字典条目,将当前名称空间中的字符串“x”映射到包含1000的整数对象的指针。当您使用x=2000更新“x”时,将创建一个新的整数对象,并更新字典以指向新对象。旧的一千个对象是不变的(可能还活着,也可能不活着,这取决于是否有任何其他对象指向该对象)。当您执行新的赋值(如y=x)时,将创建一个新的字典条目“y”,该条目指向与“x”条目相同的对象。字符串和整数等对象是不可变的。这仅仅意味着在创建对象之后,没有任何方法可以更改对象。例如,一旦整数对象一千被创建,它将永远不会改变。数学是通过创建新的整数对象来完成的。像列表这样的对象是可变的。这意味着对象的内容可以通过指向该对象的任何对象进行更改。例如,x=[];y=x;x.append(10);打印y将打印[10]。已创建空列表。“x”和“y”都指向同一列表。append方法变异(更新)列表对象(如向数据库添加记录),结果对“x”和“y”都可见(正如数据库更新对数据库的每个连接都可见)。

希望这能为您澄清问题。

数据类呢?此外,它允许您应用类型限制(也称为“类型提示”)。

from dataclasses import dataclass

@dataclass
class Holder:
    obj: your_type # Need any type? Use "obj: object" then.

def foo(ref: Holder):
    ref.obj = do_something()

我同意人们的看法,在大多数情况下,你最好考虑不要使用它。

然而,当我们谈论上下文时,我们有必要知道这一点。

不过,您可以设计显式上下文类。在进行原型设计时,我更喜欢数据类,因为来回序列化它们很容易。

干杯