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

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

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

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


当前回答

由于似乎没有任何地方提到过模拟引用的方法,例如C++就是使用一个“update”函数并传递它而不是实际变量(或者更确切地说,“name”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

这对于“仅输出引用”或具有多个线程/进程的情况(通过使更新函数线程/多处理安全)非常有用。

显然,上面不允许读取值,只允许更新它。

其他回答

它既不是通过值传递,也不是通过引用传递,而是通过对象调用。参见Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

这里有一句重要的话:

“…变量[名称]不是对象;它们不能由其他变量表示或由对象引用。”

在您的示例中,当调用Change方法时,将为其创建名称空间;var成为该名称空间中字符串对象“Original”的名称。然后,该对象在两个名称空间中都有一个名称。接下来,var='Changed'将var绑定到一个新的字符串对象,因此该方法的命名空间忘记了'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分配给函数的参数。您只能重新分配它,但它不会更改原始变量值。

Python中没有变量

理解参数传递的关键是停止思考“变量”。Python中有名称和对象看起来像变量,但始终区分这三个变量是有用的。

Python有名称和对象。赋值将名称绑定到对象。向函数传递参数还将名称(函数的参数名称)绑定到对象。

这就是它的全部。变异性与这个问题无关。

例子:

a = 1

这将名称a绑定到保存值1的integer类型的对象。

b = x

这将名称b绑定到名称x当前绑定到的同一对象。之后,名称b与名称x不再相关。

请参见Python 3语言参考中的3.1和4.2节。

如何阅读问题中的示例

在问题中显示的代码中,self.Change(self.variable)语句将名称var(在函数Change的作用域中)绑定到保存值“Original”的对象,赋值var='Changed'(在函数Change的主体中)再次将相同的名称分配给某个其他对象(恰好也包含字符串,但可能完全是其他对象)。

如何通过引用传递

因此,如果要更改的对象是可变对象,则没有问题,因为所有内容都是通过引用有效传递的。

如果它是一个不可变对象(例如布尔值、数字、字符串),那么方法是将它包装在一个可变对象中。对于这一点,快速而肮脏的解决方案是一个单元素列表(而不是self.variable,传递[self.variable]并在函数中修改var[0])。更像蟒蛇的方法是引入一个简单的单属性类。该函数接收类的实例并操作属性。

您只能使用空类作为实例来存储引用对象,因为内部对象属性存储在实例字典中。请参见示例。

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

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

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

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

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