views:

11115

answers:

9

The Python documentation seems unclear about whether parameters are passed by reference or value, and the following code produces the unchanged value 'Original'

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

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

Is there something I can do to pass the variable by actual reference?

Update:

I am coming to the conclusion that while Andrea answered my actual question (Can you... No but you can...), on the subject of pass by reference Blair Conrad is more technically correct.

As I understand it the crux is that a copy of a reference is being passed. If you assign that copy, as in my example, then you lose the reference to the original and it remains unchanged. If, however, you 'use' that reference, for example append on a passed list, then the original is changed.

I will see how the comments and votes go before choosing the answer people think is the best

+2  A: 

For a short explanation/clarification see the first answer to this stackoverflow question. As strings are immutable, they won't be changed and a new variable will be created, thus the "outer" variable still has the same value.

PhilS
+1  A: 

In this case the variable titled var in the method Change is assigned a reference to self.variable, and you immediately assign a string to var. It's no longer pointing to self.variable. The following code snippet shows what would happen if you modify the data structure pointed to by var and self.variable, in this case a list:

>>> 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']
>>>

I'm sure someone else could clarify this further.

Mike Mazur
+50  A: 

Parameters are passed by value. The reason people are confused by the behaviour is twofold:

  1. the parameter passed in is actually a reference to a variable (but the reference is passed by value)
  2. some data types are mutable, but others aren't

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 heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object. If out pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.

Okay, this is a little confusing. Let's have some examples.

List - a mutable type

Let's 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 let's 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

It's immutable, so there's nothing we can do to change the contents of the string

Now, let's 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 list, but there was no way to change where outer_string pointed.

I hope this clears things up a little.

EDIT: It's been noted that this doesn't answer the question that @David originally asked, "Is there something I can do to pass the variable by actual reference?". Let's work on that.

How do we get around this?

As @Andrea's answer shows, you could return the new value. This doesn't 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.

Blair Conrad
Then the same is in C, when you pass "by reference" you're actually passing _by value_ the reference... Define "by reference" :P
Andrea Ambu
I'm not sure I understand your terms. I've been out of the C game for a while, but back when I was in it, there was no "pass by reference" - you could pass things, and it was always pass by value, so whatever was in the parameter list was copied. But sometimes the thing was a pointer, which one could follow to the piece of memory (primitive, array, struct, whatever), but you couldn't change the pointer that was copied from the outer scope - when you were done with the function, the original pointer still pointed to the same address. C++ introduced references, which behaved differently.
Blair Conrad
@andrea, not it is not like C at all. Conrad is correct, but the terms reference/values are confusing in python. That's why you should really use another term (see my link to effbot for a good explanation)
David Cournapeau
your answer is most enlightening. big help for me learning python. big +1.
Doug T.
+1  A: 

You got some really good answers here.

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' )
bobobobo
+4  A: 

Think of stuff being passed by assignment instead of by reference/by value. That way, it is allways clear, what is happening as long as you understand what happens during normal assignment.

So, when passing a list to a function/method, the list is assigned to the parameter name. Appending to the list will result in the list being modified. Reassigning the list inside the function will not change the original list, since:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Since immutable types cannot be modified, they seem like being passed by value - passing an int into a function means assigning the int to the functions parameter. You can only ever reassign that, but it won't change the originial variables value.

Daren Thomas
+11  A: 

It is neither pass-by-value or pass-by-reference - it is call-by-object. See this, by Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

David Cournapeau
A: 

You can use the slice notation to get the behavior you expected:

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

    def Change(self, var):
        var[:] = 'Changed'
recursive
That example does not work, you'll get: `TypeError: 'str' object does not support item assignment`
Mattias Nilsson
+1  A: 

If my understanding is correct, then I think it is important to note that the current highest point post (by Blair Conrad) is correct in its' result but misleading, bordering on incorrect in its definitions. For anyone who is accustomed to C (or many other languages) that fall into the pass by reference or pass by value classifications, Python is not either of those.

David Cournapeau's answer points to the real answer which explains why the behavior in Blair Conrad's post is what it is, while the definitions are not.

To the extent that Python is pass by value, all languages are pass by value since some piece of data (be it a "value" or a "reference") must be sent. However, that does not mean that Python is pass by value in the sense that a C programmer would think of it.

All that to say if you want the behavior, Blair Conrad's answer is fine. If you want to know the nuts and bolts of why Python is neither pass by value or reference, read David Cournapeau's answer.

If I have misunderstood the way Python works, any corrections would be appreciated.

yakiimo
Yes, I think the link David Cournapeau gives is the more complete description
David Sykes
A: 

Python string are immutable so any assignment sets a reference to a new string object in the memory. You can go around it by using MutableString and [:] assignment in your example:

from UserString import MutableString

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

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