参数是通过引用传递还是通过值传递?如何通过引用传递,以便下面的代码输出“Changed”而不是“Original”?
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
另请参阅:为什么函数可以修改调用者感知的某些参数,而不能修改其他参数?
如果没有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'
Python为每个对象分配一个唯一的标识符,可以使用Python的内置id()函数找到该标识符。可以验证函数调用中的实际参数和正式参数是否具有相同的id值,这表明伪参数和实际参数引用了相同的对象。注意,实际参数和对应的伪参数是引用同一对象的两个名称。如果将伪参数重新绑定到函数范围中的新值/对象,这不会影响实际参数仍然指向原始对象的事实,因为实际参数和伪参数是两个名称。以上两个事实可以概括为“参数通过赋值传递”。即。,
dummy_argument = actual_argument
如果将dummy_argument重新绑定到函数体中的新对象,则actual_argument仍然引用原始对象。如果使用dummy_argument[0]=some_thing,那么这也将修改actual_argument[0]。因此,“通过引用传递”的效果可以通过修改传入的对象引用的组件/属性来实现。当然,这需要传递的对象是可变对象。
为了与其他语言进行比较,您可以说Python以与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中没有变量
理解参数传递的关键是停止思考“变量”。Python中有名称和对象看起来像变量,但始终区分这三个变量是有用的。
Python有名称和对象。赋值将名称绑定到对象。向函数传递参数还将名称(函数的参数名称)绑定到对象。
这就是它的全部。变异性与这个问题无关。
例子:
a = 1
这将名称a绑定到保存值1的integer类型的对象。
b = x
这将名称b绑定到名称x当前绑定到的同一对象。之后,名称b与名称x不再相关。
请参见Python 3语言参考中的3.1和4.2节。
如何阅读问题中的示例
在问题中显示的代码中,self.Change(self.variable)语句将名称var(在函数Change的作用域中)绑定到保存值“Original”的对象,赋值var='Changed'(在函数Change的主体中)再次将相同的名称分配给某个其他对象(恰好也包含字符串,但可能完全是其他对象)。
如何通过引用传递
因此,如果要更改的对象是可变对象,则没有问题,因为所有内容都是通过引用有效传递的。
如果它是一个不可变对象(例如布尔值、数字、字符串),那么方法是将它包装在一个可变对象中。对于这一点,快速而肮脏的解决方案是一个单元素列表(而不是self.variable,传递[self.variable]并在函数中修改var[0])。更像蟒蛇的方法是引入一个简单的单属性类。该函数接收类的实例并操作属性。