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

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

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

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


当前回答

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

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

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

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

其他回答

从技术上讲,Python始终使用引用传递值。我将重复我的另一个回答,以支持我的发言。

Python始终使用引用传递值。没有任何例外。任何变量赋值都意味着复制参考值。没有例外。任何变量都是绑定到引用值的名称。总是

您可以将参考值视为目标对象的地址。地址在使用时自动取消引用。这样,使用引用值时,似乎可以直接使用目标对象。但在两者之间总是有一个参考点,多跳一步就可以到达目标。

下面的示例证明了Python使用的是通过引用传递:

如果参数是按值传递的,则无法修改外部lst。绿色是目标对象(黑色是存储在内存中的值,红色是对象类型),黄色是内存中的参考值,如箭头所示。蓝色实心箭头是传递给函数的参考值(通过蓝色虚线箭头路径)。丑陋的深黄色是内部字典。(实际上它也可以画成一个绿色椭圆。颜色和形状只表示它是内部的。)

您可以使用id()内置函数来了解引用值是什么(即目标对象的地址)。

在编译语言中,变量是能够捕获类型值的内存空间。在Python中,变量是绑定到引用变量的名称(内部捕获为字符串),该引用变量保存目标对象的引用值。变量的名称是内部字典中的键,该字典项的值部分存储目标的引用值。

引用值在Python中隐藏。没有任何用于存储引用值的显式用户类型。但是,您可以使用列表元素(或任何其他合适容器类型的元素)作为引用变量,因为所有容器都将元素存储为对目标对象的引用。换句话说,元素实际上不包含在容器中——只有对元素的引用。

我是Python新手,从昨天开始(尽管我已经编程45年了)。

我来这里是因为我在写一个函数,希望有两个所谓的参数。如果它只是一个out参数,我现在就不会在检查reference/value在Python中的工作方式时感到困惑。我只需要使用函数的返回值。但由于我需要两个这样的参数,我觉得我需要整理一下。

在这篇文章中,我将展示我如何解决我的问题。也许其他来到这里的人会觉得它很有价值,尽管它并不是对主题问题的答案。当然,经验丰富的Python程序员已经知道我使用的解决方案,但这对我来说是新的。

从这里的答案中,我可以很快看出Python在这方面的工作方式有点像Javascript,如果您想要引用功能,则需要使用变通方法。

但后来我在Python中发现了一些我以前在其他语言中没有见过的东西,即可以用简单的逗号分隔方式从函数中返回多个值,如下所示:

def somefunction(p):
    a=p+1
    b=p+2
    c=-p
    return a, b, c

并且你可以在调用端类似地处理它,就像这样

x, y, z = somefunction(w)

这对我来说已经足够好了,我很满意。不需要使用一些变通方法。

在其他语言中,您当然也可以返回许多值,但通常是从对象中返回,您需要相应地调整调用端。

Python的方法很好,也很简单。

如果您想通过引用进行更多模拟,可以执行以下操作:

def somefunction(a, b, c):
    a = a * 2
    b = b + a
    c = a * b * c
    return a, b, c

x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")

x, y, z = somefunction(x, y, z)

print(F"After  : {x}, {y}, {z}")

这给出了这个结果

Before : 3, 5, 10  
After  : 6, 11, 660  

给定python处理值和对它们的引用的方式,唯一可以引用任意实例属性的方法是通过名称:

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

    def change(self, var):
        self.__dict__[var] = 'Changed'

当然,在实际代码中,您可以在dict查找中添加错误检查。

很可能不是最可靠的方法,但这是有效的,请记住,您正在重载内置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)

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

dummy_argument = actual_argument

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

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