Python 如何通过引用传递变量?
Python文档似乎不清楚参数是通过引用还是通过值传递的,下面的代码生成未更改的值“Original”Python 如何通过引用传递变量?,python,reference,parameter-passing,pass-by-reference,Python,Reference,Parameter Passing,Pass By Reference,Python文档似乎不清楚参数是通过引用还是通过值传递的,下面的代码生成未更改的值“Original” class PassByReference: def __init__(self): self.variable = 'Original' self.change(self.variable) print(self.variable) def change(self, var): var = 'Changed'
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
我可以通过实际引用传递变量吗?在这种情况下,方法
Change
中标题为var
的变量被指定为self.variable
的引用,您可以立即为var
指定一个字符串。它不再指向self.variable
。下面的代码片段显示了如果您修改var
和self.variable
所指向的数据结构会发生什么,在本例中是一个列表:
>>> 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']
>>>
我相信其他人可以进一步澄清这一点。论点是正确的。这背后的理由有两个:
- 如果将一个可变对象传递给一个方法,该方法将获得对该对象的引用,您可以随心所欲地对其进行变异,但如果您在该方法中重新绑定该引用,外部范围将对此一无所知,并且在您完成后,外部引用仍将指向原始对象
- 如果将一个不可变对象传递给一个方法,则仍然无法重新绑定外部引用,甚至无法修改该对象
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
输出:
before,outer_list=['1','2','3']
得到['1','2','3']
更改为['1','2','3','4']
之后,外部列表=['1'、'2'、'3'、'4']
由于传入的参数是对outer\u list
的引用,而不是它的副本,因此我们可以使用mutating list方法对其进行更改,并将更改反映在外部范围中
现在让我们看看当我们试图更改作为参数传入的引用时会发生什么:
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
输出:
before,outer_list=['we','like','property','English']
得到['we'、'like'、'property'、'English']
设置为[‘和’、‘我们’、‘能’、‘不能’、‘撒谎’]
在后面,outer_list=['we','like','property','English']
由于the_list
参数是按值传递的,因此为其分配一个新的列表没有方法之外的代码可以看到的效果。外部列表
是外部列表
引用的副本,我们将外部列表
指向一个新列表,但无法更改外部列表
指向的位置
字符串-不可变的类型
它是不可变的,因此我们无法更改字符串的内容
现在,让我们尝试更改引用
def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)
outer_string = 'It was many and many a year ago'
print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)
输出:
以前,outer_string=这是很多很多年前的事了
一年前就有很多了
去海边的王国
之后,outer_string=这是很多很多年前的事了
同样,由于参数是按值传递的,因此给它分配一个新的字符串没有方法之外的代码可以看到的效果。外部字符串
是外部字符串
引用的副本,我们将外部字符串
指向一个新字符串,但无法更改外部字符串
指向的位置
我希望这能把事情弄清楚一点
编辑:需要注意的是,这并没有回答@David最初提出的问题:“我能做些什么来通过实际引用传递变量吗?”。让我们努力吧
我们该怎么解决这个问题?
正如@Andrea的回答所示,您可以返回新值。这不会改变传递信息的方式,但会让您获得想要的信息:
def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string
# then you could call it like
my_string = return_a_whole_new_string(my_string)
如果您确实希望避免使用返回值,可以创建一个类来保存您的值并将其传递到函数中,或者使用现有类,如列表:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string
# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)
do_something_with(wrapper[0])
虽然这看起来有点麻烦。您在这里得到了一些非常好的答案
x = [ 2, 4, 4, 5, 5 ]
print x # 2, 4, 4, 5, 5
def go( li ) :
li = [ 5, 6, 7, 8 ] # re-assigning what li POINTS TO, does not
# change the value of the ORIGINAL variable x
go( x )
print x # 2, 4, 4, 5, 5 [ STILL! ]
raw_input( 'press any key to continue' )
设想通过赋值传递,而不是通过引用/值传递。这样,只要你了解正常作业期间发生的事情,就会清楚发生了什么
因此,当将列表传递给函数/方法时,该列表被指定给参数名。添加到列表将导致列表被修改。在函数内重新分配列表不会更改原始列表,因为:
a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b # prints [1, 2, 3, 4] ['a', 'b']
由于不可变类型无法修改,它们看起来像是通过值传递的——将int传递给函数意味着将int分配给函数的参数。您只能重新分配它,但它不会更改原始变量值。它既不是按值传递也不是按引用传递-它是按对象调用的。见Fredrik Lundh所著:
这里有一句重要的话:
“……变量[名称]不是对象;它们不能由其他变量表示或由对象引用。”
在您的示例中,当调用Change
方法时,会为其创建一个;而var
成为该名称空间中字符串对象'Original'
的名称。然后,该对象在两个名称空间中有一个名称。接下来,var='Changed'
将var
绑定到一个新的字符串对象,因此该方法的名称空间忘记了'Original'
。最后,这个名称空间被遗忘了,字符串也随之“改变”
。(编辑-布莱尔更新了他广受欢迎的答案,因此现在它是准确的)
我认为重要的是要注意到,目前投票最多的帖子(由布莱尔·康拉德撰写)虽然在结果上是正确的,但它具有误导性,几乎是不正确的
def __init__(self):
self.variable = 'Original'
self.Change(self.variable)
def Change(self, var):
var = 'Changed'
def __init__(self):
self.variable = ['Original']
self.Change(self.variable)
def Change(self, var):
var[0] = 'Changed'
def change_me(list):
list = [1, 2, 3]
my_list = [0, 1]
change_me(my_list)
[0, 1] = [1, 2, 3]
def change_me(list):
list.append(2)
[0, 1].append(2)
>>> def x(y):
... global z
... z = y
...
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined
>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2
a = 1
b = x
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
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.Change()
print self.variable
def Change(self):
self.variable = 'Changed'
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.variable = PassByReference.Change(self.variable)
print self.variable
@staticmethod
def Change(var):
var = 'Changed'
return var
class PassByReference:
def __init__(self, name):
self.name = name
def changeRef(ref):
ref[0] = PassByReference('Michael')
obj = PassByReference('Peter')
print obj.name
p = [obj] # A pointer to obj! ;-)
changeRef(p)
print p[0].name # p->name
a=0
b=0
c=0
def myfunc(a,b,c):
a=1
b=2
c=3
return a,b,c
a,b,c = myfunc(a,b,c)
print a,b,c
def change(wrapper):
wrapper(7)
x = 5
def setter(val):
global x
x = val
print(x)
class ByRef:
def __init__(self, r, w, d):
self._read = r
self._write = w
self._delete = d
def set(self, val):
self._write(val)
def get(self):
return self._read()
def remove(self):
self._delete()
wrapped = property(get, set, remove)
# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15
class ByRef:
def __init__(self, locs, name):
self._locs = locs
self._name = name
def set(self, val):
self._locs[self._name] = val
def get(self):
return self._locs[self._name]
def remove(self):
del self._locs[self._name]
wrapped = property(get, set, remove)
def change(x):
x.wrapped = 7
def test_me():
x = 6
print(x)
change(ByRef(locals(), "x"))
print(x)
class PassByReferenceIsh:
def __init__(self):
self.variable = 'Original'
self.change('variable')
print self.variable
def change(self, var):
self.__dict__[var] = 'Changed'
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change('variable')
print(self.variable)
def change(self, var):
setattr(self, var, 'Changed')
# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == '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)
# 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
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
def somefunction(p):
a=p+1
b=p+2
c=-p
return a, b, c
x, y, z = somefunction(w)
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
import ctypes
def f(a):
a.value=2398 ## resign the value in a function
a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)
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)
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'