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

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

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

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


当前回答

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

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)

其他回答

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

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

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

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

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

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

这里的答案有很多见解,但我认为这里没有明确提到另外一点。引用python文档https://docs.python.org/2/faq/programming.html#what-是python中局部和全局变量的规则

“在Python中,仅在函数内部引用的变量是隐式全局的。如果在函数体中的任何位置为变量分配了新值,则假定该变量是局部的。如果变量在函数内部被分配了新的值,则该变量隐式是局部的,您需要将其显式声明为“全局”。虽然一开始有点令人惊讶,但片刻的考虑可以解释这一点。一方面,要求全局分配变量可以防止意外的副作用。另一方面,如果所有全局引用都需要全局引用,那么您将一直使用全局引用。您必须将对内置函数或导入模块组件的每个引用声明为全局引用。这种混乱将破坏全球宣言在确定副作用方面的作用。"

即使在将可变对象传递给函数时,这仍然适用。对我来说,这清楚地解释了分配给对象和在函数中操作对象之间行为差异的原因。

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

给予:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

因此,对未声明为全局的全局变量的赋值将创建一个新的局部对象,并断开与原始对象的链接。

大多数时候,要通过引用传递的变量是类成员。我建议的解决方案是使用修饰符来添加可变字段和相应的属性。该字段是变量的类包装器。

@refproperty同时添加self_myvar(可变)和self.myvar属性。

@refproperty('myvar')
class T():
    pass

def f(x):
   x.value=6

y=T()
y.myvar=3
f(y._myvar)
print(y.myvar) 

它将打印6。

将其与以下内容进行比较:

class X:
   pass

x=X()
x.myvar=4

def f(y):
    y=6

f(x.myvar)
print(x.myvar) 

在这种情况下,它不起作用。它将打印4。

代码如下:

def refproperty(var,value=None):
    def getp(self):
        return getattr(self,'_'+var).get(self)

    def setp(self,v):
        return getattr(self,'_'+var).set(self,v)

    def decorator(klass):
        orginit=klass.__init__
        setattr(klass,var,property(getp,setp))

        def newinit(self,*args,**kw):
            rv=RefVar(value)
            setattr(self,'_'+var,rv)
            orginit(self,*args,**kw)

        klass.__init__=newinit
        return klass
    return decorator

class RefVar(object):
    def __init__(self, value=None):
        self.value = value
    def get(self,*args):
        return self.value
    def set(self,main, value):
        self.value = value

我是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