By value or by reference?

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Mon Oct 18 12:26:49 EDT 2004


Riccardo Rossi wrote:
> Hi all!
> 
> How does Python pass arguments to a function? By value or by reference?

Both...
Err, no, none.
Well...

*Short answer :*
args are passed by ref, but bindings are local.

*Long answer :*
You first need to understand what 'variables' in Python are. They are in 
fact just a symbol referencing an object. Think of a Python 'variable' 
as an entry in a dict, the name of the variable (the 'symbol') being the 
key and the reference to the object being the value (AFAIK, this is 
exactly what they are).

You also need to understand the difference between mutable and immutable 
objects. Strings, numerics and tuples are immutables. Which means that 
you can not change them. When doing :
s = "Hello "
s += "World"
... you are not modifying the string object bound to s, but creating a 
new string object and binding it to s.

Now for the "By Val/By Ref" stuff... well, try this :

def my_fun(my_arg):
     print "in my_fun, before rebinding my_arg: my_arg = %s" % my_arg
     my_arg = "42"
     print "in my_fun, after rebinding my_arg: my_arg = %s" % my_arg

the_arg = "Life, the Universe, and Everything"

print "before call to my_fun: the_arg = %s" % the_arg
my_fun(the_arg)
print "after call to my_fun: the_arg = %s" % the_arg


You should have somthing like this :

before call to my_fun: the_arg = Life, the Universe, and Everything
in my_fun, before rebinding my_arg: my_arg = Life, the Universe, and 
Everything
in my_fun, after rebinding my_arg: my_arg = 42
after call to my_fun: the_arg = Life, the Universe, and Everything

So what ? are we passing args by value ? Well, retry with :

def my_fun_2(my_arg_2):
     print "in my_fun, before appending to my_arg_2: my_arg_2 = %s" % 
my_arg_2
     my_arg_2.append("42")
     print "in my_fun, after appending to my_arg_2: my_arg_2 = %s" % 
my_arg_2

the_arg_2 = ["Life, the Universe, and Everything"]

print "before call to my_fun_2: the_arg_2 = %s" % the_arg_2
my_fun_2(the_arg_2)
print "after call to my_fun_2: the_arg_2 = %s" % the_arg_2

and what do we get ?

before call to my_fun_2: the_arg_2 = ['Life, the Universe, and Everything']
in my_fun, before appending to my_arg_2: my_arg_2 = ['Life, the 
Universe, and Everything']
in my_fun, after appending to my_arg_2: my_arg_2 = ['Life, the Universe, 
and Everything', '42']
after call to my_fun_2: the_arg_2 = ['Life, the Universe, and 
Everything', '42']

Argh ! So what ? Is it by val or by ref ?
Ok, let's try a third test :

def my_fun_3(my_arg_3):
     print "in my_fun, before rebinding my_arg_3: my_arg_3 = %s" % my_arg_3
     my_arg_3 = ["42"]
     print "in my_fun, after rebinding my_arg_3: my_arg_3 = %s" % my_arg_3

the_arg_3 = ["Life, the Universe, and Everything"]

print "before call to my_fun_3: the_arg_3 = %s" % the_arg_3
my_fun_3(the_arg_3)
print "after call to my_fun_3: the_arg_3 = %s" % the_arg_3

An you get :
before call to my_fun_3: the_arg_3 = ['Life, the Universe, and Everything']
in my_fun, before rebinding my_arg_3: my_arg_3 = ['Life, the Universe, 
and Everything']
in my_fun, after rebinding my_arg_3: my_arg_3 = ['42']
after call to my_fun_3: the_arg_3 = ['Life, the Universe, and Everything']

err... Time for some explanation ?-)

When you call a function with an arg, a "local variable" is created, 
which references the object passed as the argument. (well... an entry 
with the formal parameter name as key and a reference to the object 
passed in is created in the 'local' dict).

So, rebinding this local symbol does not impact the binding in the 
caller's namespace - because the symbol lives in another namespace.

*But* - and if the object referenced is mutable of course - modifying 
the object in the function... well, just modifies the object, because 
it's the *same* object that is bound to ('referenced by', if you prefer) 
both symbols (the one in the caller's namespace and the one in the 
function's namespace). So yes, the object *is* modified when the 
function returns.

(Of course, doing anything to an immutable object is just rebinding the 
symbol, so it won't never have any impact outside the function. So don't 
expect to have a function having side-effects on strings, numerics and 
tuples args. The good news being that this is not necessary, since the 
only reason to do so would be to return multiple values, and Python can 
already return multiple values.)


Does it make sens to you ?-)

> Thanks,
> Riccardo Rossi. 
> 

HTH
Bruno

(Ho, BTW, please read the fine manual - and this too :
http://starship.python.net/crew/mwh/hacks/objectthink.html)




More information about the Python-list mailing list