参数是通过引用传递还是通过值传递?如何通过引用传递,以便下面的代码输出“Changed”而不是“Original”?
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
另请参阅:为什么函数可以修改调用者感知的某些参数,而不能修改其他参数?
从技术上讲,Python始终使用引用传递值。我将重复我的另一个回答,以支持我的发言。
Python始终使用引用传递值。没有任何例外。任何变量赋值都意味着复制参考值。没有例外。任何变量都是绑定到引用值的名称。总是
您可以将参考值视为目标对象的地址。地址在使用时自动取消引用。这样,使用引用值时,似乎可以直接使用目标对象。但在两者之间总是有一个参考点,多跳一步就可以到达目标。
下面的示例证明了Python使用的是通过引用传递:
如果参数是按值传递的,则无法修改外部lst。绿色是目标对象(黑色是存储在内存中的值,红色是对象类型),黄色是内存中的参考值,如箭头所示。蓝色实心箭头是传递给函数的参考值(通过蓝色虚线箭头路径)。丑陋的深黄色是内部字典。(实际上它也可以画成一个绿色椭圆。颜色和形状只表示它是内部的。)
您可以使用id()内置函数来了解引用值是什么(即目标对象的地址)。
在编译语言中,变量是能够捕获类型值的内存空间。在Python中,变量是绑定到引用变量的名称(内部捕获为字符串),该引用变量保存目标对象的引用值。变量的名称是内部字典中的键,该字典项的值部分存储目标的引用值。
引用值在Python中隐藏。没有任何用于存储引用值的显式用户类型。但是,您可以使用列表元素(或任何其他合适容器类型的元素)作为引用变量,因为所有容器都将元素存储为对目标对象的引用。换句话说,元素实际上不包含在容器中——只有对元素的引用。
如果没有Python中的此功能,这可能是一个优雅的面向对象解决方案。一个更优雅的解决方案是让您从中创建子类。或者你可以把它命名为“大师班”。但是不要有一个变量和一个布尔值,让它们成为某种类型的集合。我修改了实例变量的命名,以符合PEP8。
class PassByReference:
def __init__(self, variable, pass_by_reference=True):
self._variable_original = 'Original'
self._variable = variable
self._pass_by_reference = pass_by_reference # False => pass_by_value
self.change(self.variable)
print(self)
def __str__(self):
print(self.get_variable())
def get_variable(self):
if pass_by_reference == True:
return self._variable
else:
return self._variable_original
def set_variable(self, something):
self._variable = something
def change(self, var):
self.set_variable(var)
def caller_method():
pbr = PassByReference(variable='Changed') # this will print 'Changed'
variable = pbr.get_variable() # this will assign value 'Changed'
pbr2 = PassByReference(variable='Changed', pass_by_reference=False) # this will print 'Original'
variable2 = pbr2.get_variable() # this will assign value 'Original'
大多数时候,要通过引用传递的变量是类成员。我建议的解决方案是使用修饰符来添加可变字段和相应的属性。该字段是变量的类包装器。
@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
由于字典是通过引用传递的,所以可以使用dict变量在其中存储任何引用的值。
# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
result = a + b
ref['multi'] = a * b # reference the multi. ref['multi'] is number
ref['msg'] = "The result: " + str(result) + " was nice!"
return result
number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.
sum = AddNumbers(number1, number2, ref)
print("sum: ", sum) # the returned value
print("multi: ", ref['multi']) # a referenced value
print("msg: ", ref['msg']) # a referenced value