python – How do I pass a variable by reference?
python – How do I pass a variable by reference?
Arguments are passed by assignment. The rationale behind this is twofold:
- the parameter passed in is actually a reference to an object (but the reference is passed by value)
- some data types are mutable, but others arent
So:
- If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your hearts delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after youre done, the outer reference will still point at the original object.
- If you pass an immutable object to a method, you still cant rebind the outer reference, and you cant even mutate the object.
To make it even more clear, lets have some examples.
List – a mutable type
Lets try to modify the list that was passed to a method:
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)
Output:
before, outer_list = [one, two, three]
got [one, two, three]
changed to [one, two, three, four]
after, outer_list = [one, two, three, four]
Since the parameter passed in is a reference to outer_list
, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.
Now lets see what happens when we try to change the reference that was passed in as a parameter:
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)
Output:
before, outer_list = [we, like, proper, English]
got [we, like, proper, English]
set to [and, we, can, not, lie]
after, outer_list = [we, like, proper, English]
Since the the_list
parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list
was a copy of the outer_list
reference, and we had the_list
point to a new list, but there was no way to change where outer_list
pointed.
String – an immutable type
Its immutable, so theres nothing we can do to change the contents of the string
Now, lets try to change the reference
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)
Output:
before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago
Again, since the the_string
parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string
was a copy of the outer_string
reference, and we had the_string
point to a new string, but there was no way to change where outer_string
pointed.
I hope this clears things up a little.
EDIT: Its been noted that this doesnt answer the question that @David originally asked, Is there something I can do to pass the variable by actual reference?. Lets work on that.
How do we get around this?
As @Andreas answer shows, you could return the new value. This doesnt change the way things are passed in, but does let you get the information you want back out:
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)
If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:
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])
Although this seems a little cumbersome.
The problem comes from a misunderstanding of what variables are in Python. If youre used to most traditional languages, you have a mental model of what happens in the following sequence:
a = 1
a = 2
You believe that a
is a memory location that stores the value 1
, then is updated to store the value 2
. Thats not how things work in Python. Rather, a
starts as a reference to an object with the value 1
, then gets reassigned as a reference to an object with the value 2
. Those two objects may continue to coexist even though a
doesnt refer to the first one anymore; in fact they may be shared by any number of other references within the program.
When you call a function with a parameter, a new reference is created that refers to the object passed in. This is separate from the reference that was used in the function call, so theres no way to update that reference and make it refer to a new object. In your example:
def __init__(self):
self.variable = Original
self.Change(self.variable)
def Change(self, var):
var = Changed
self.variable
is a reference to the string object Original
. When you call Change
you create a second reference var
to the object. Inside the function you reassign the reference var
to a different string object Changed
, but the reference self.variable
is separate and does not change.
The only way around this is to pass a mutable object. Because both references refer to the same object, any changes to the object are reflected in both places.
def __init__(self):
self.variable = [Original]
self.Change(self.variable)
def Change(self, var):
var[0] = Changed
python – How do I pass a variable by reference?
I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters.