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

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

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

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


当前回答

我分享了另一种有趣的方式,让人们通过一个方便的工具来理解这个主题——基于@Mark Ransom传递可变列表的示例,可视化巨蟒代码执行

随便玩玩,你就会明白的。

传递字符串

传递列表

其他回答

在这种情况下,方法Change中名为var的变量被分配了对self.variable的引用,并且您立即将字符串分配给var。它不再指向self.variable.下面的代码片段显示了如果您修改了var和self.variaable指向的数据结构(在本例中是一个列表)会发生什么:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

我相信其他人可以进一步澄清这一点。

想想通过赋值而不是通过引用/值传递的东西。这样,只要你明白在正常任务中发生了什么,就会很清楚发生了什么。

因此,当将列表传递给函数/方法时,该列表被分配给参数名称。附加到列表将导致列表被修改。重新分配函数内的列表不会更改原始列表,因为:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

由于不可变类型不能被修改,它们看起来像是通过值传递的——将int传递给函数意味着将int分配给函数的参数。您只能重新分配它,但它不会更改原始变量值。

由于字典是通过引用传递的,所以可以使用dict变量在其中存储任何引用的值。

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!"
    return result

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the returned value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

由于您的示例恰好是面向对象的,因此可以进行以下更改以获得类似的结果:

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

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

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

Python的传递赋值方案与C++的引用参数选项并不完全相同,但实际上它与C语言(以及其他语言)的参数传递模型非常相似:

不可变的参数实际上是“按值”传递的。整数和字符串等对象是通过对象引用传递的,而不是通过复制传递的,但因为无论如何都不能在原地更改不可变的对象,所以效果很像复制。可变参数是“通过指针”有效传递的字典也通过对象引用传递,这与C的方式类似传递数组作为指针,可变对象可以在函数中的适当位置改变,很像C阵列。