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

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

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

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


当前回答

def i_my_wstring_length(wstring_input:str = "", i_length:int = 0) -> int:
    i_length[0] = len(wstring_input)
    return 0

wstring_test  = "Test message with 32 characters."
i_length_test = [0]
i_my_wstring_length(wstring_test, i_length_test)
print("The string:\n\"{}\"\ncontains {} character(s).".format(wstring_test, *i_length_test))
input("\nPress ENTER key to continue . . . ")

其他回答

我使用以下方法将一些Fortran代码快速转换为Python。的确,它不像最初提出的问题那样通过引用传递,但在某些情况下是一个简单的解决方案。

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

问题来自对Python中变量的误解。如果你习惯了大多数传统语言,你会有一个心理模型来描述以下顺序:

a = 1
a = 2

您认为a是存储值1的内存位置,然后更新为存储值2。这不是Python中的工作方式。相反,a开始作为对值为1的对象的引用,然后重新分配为对值为2的对象的参考。这两个对象可能会继续共存,即使a不再指代第一个对象;事实上,它们可以由程序内的任何数量的其他引用共享。

使用参数调用函数时,将创建一个引用传入对象的新引用。这与函数调用中使用的引用不同,因此无法更新该引用并使其引用新对象。在您的示例中:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

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

self.variable是对字符串对象“Original”的引用。当调用Change时,将创建对象的第二个引用变量。在函数内部,您将引用变量重新分配给不同的字符串对象“Changed”,但引用self.variable是独立的,不会更改。

解决此问题的唯一方法是传递一个可变对象。因为两个引用都引用同一个对象,所以对对象的任何更改都会反映在两个位置。

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

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

下面是Python中使用的pass by对象概念的简单解释(我希望)。每当您将对象传递给函数时,对象本身就会被传递(Python中的对象实际上是其他编程语言中的值),而不是对该对象的引用。换句话说,当您拨打电话时:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

正在传递实际对象-[0,1](在其他编程语言中称为值)。因此,实际上函数change_me将尝试执行以下操作:

[0, 1] = [1, 2, 3]

这显然不会改变传递给函数的对象。如果函数看起来像这样:

def change_me(list):
   list.append(2)

那么该调用将导致:

[0, 1].append(2)

这显然会改变对象。这个答案很好地解释了这一点。

由于似乎没有任何地方提到过模拟引用的方法,例如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

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

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

我解决了类似的要求,如下所示:

要实现更改变量的成员函数,请不要传递变量本身,而是传递包含引用该变量的setattr的functools.partial。在change()内调用functools.partial将执行setttr并更改实际引用的变量。

注意,setattr需要变量的名称作为字符串。

class PassByReference(object):
    def __init__(self):
        self.variable = "Original"
        print(self.variable)        
        self.change(partial(setattr,self,"variable"))
        print(self.variable)

    def change(self, setter):
        setter("Changed")