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

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

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

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


当前回答

很可能不是最可靠的方法,但这是有效的,请记住,您正在重载内置str函数,这通常是您不想做的事情:

import builtins

class sstr(str):
    def __str__(self):
        if hasattr(self, 'changed'):
            return self.changed

        return self

    def change(self, value):
        self.changed = value

builtins.str = sstr

def change_the_value(val):
    val.change('After')

val = str('Before')
print (val)
change_the_value(val)
print (val)

其他回答

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

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

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

Python为每个对象分配一个唯一的标识符,可以使用Python的内置id()函数找到该标识符。可以验证函数调用中的实际参数和正式参数是否具有相同的id值,这表明伪参数和实际参数引用了相同的对象。注意,实际参数和对应的伪参数是引用同一对象的两个名称。如果将伪参数重新绑定到函数范围中的新值/对象,这不会影响实际参数仍然指向原始对象的事实,因为实际参数和伪参数是两个名称。以上两个事实可以概括为“参数通过赋值传递”。即。,

dummy_argument = actual_argument

如果将dummy_argument重新绑定到函数体中的新对象,则actual_argument仍然引用原始对象。如果使用dummy_argument[0]=some_thing,那么这也将修改actual_argument[0]。因此,“通过引用传递”的效果可以通过修改传入的对象引用的组件/属性来实现。当然,这需要传递的对象是可变对象。

为了与其他语言进行比较,您可以说Python以与C相同的方式按值传递参数,其中当您“按引用”传递时,实际上是按值传递引用(即指针)

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

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()

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

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

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

干杯

在这种情况下,方法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']
>>> 

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

关于这一点,已经有很多很好的答案(或者说是观点),我已经读过了,但我想提到一个缺失的答案。常见问题解答部分Python文档中的一个。我不知道发布此页面的日期,但这应该是我们的真实参考:

记住,在Python中,参数是通过赋值传递的。自从赋值只创建对对象的引用,没有别名在调用者和被调用者中的参数名之间,因此没有引用调用本身。

如果您有:

a = SOMETHING

def fn(arg):
    pass

你把它叫做fn(a),你做的和你在作业中做的完全一样。所以会发生这种情况:

arg = a

创建了对SOMETHING的其他引用。变量只是符号/名称/引用。他们不“持有”任何东西。