I am not sure I understand the concept of Python's call by object style of passing function arguments (explained here http://effbot.org/zone/call-by-object.htm). There don't seem to be enough examples to clarify this concept well (or my google-fu is probably weak! :D)
I wrote this little contrived Python program to try to understand this concept
def foo( itnumber, ittuple, itlist, itdict ):
itnumber +=1
print id(itnumber) , itnumber
print id(ittuple) , ittuple
itlist.append(3.4)
print id(itlist) , itlist
itdict['mary'] = 2.3
print id(itdict), itdict
# Initialize a number, a tuple, a list and a dictionary
tnumber = 1
print id( tnumber ), tnumber
ttuple = (1, 2, 3)
print id( ttuple ) , ttuple
tlist = [1, 2, 3]
print id( tlist ) , tlist
tdict = tel = {'jack': 4098, 'sape': 4139}
print '-------'
# Invoke a function and test it
foo(tnumber, ttuple, tlist , tdict)
print '-------'
#Test behaviour after the function call is over
print id(tnumber) , tnumber
print id(ttuple) , ttuple
print id(tlist) , tlist
print id(tdict), tdict
The output of the program is
146739376 1
3075201660 (1, 2, 3)
3075103916 [1, 2, 3]
3075193004 {'sape': 4139, 'jack': 4098}
---------
146739364 2
3075201660 (1, 2, 3)
3075103916 [1, 2, 3, 3.4]
3075193004 {'sape': 4139, 'jack': 4098, 'mary': 2.3}
---------
146739376 1
3075201660 (1, 2, 3)
3075103916 [1, 2, 3, 3.4]
3075193004 {'sape': 4139, 'jack': 4098, 'mary': 2.3}
As you can see , except for the integer that was passed, the object id's (which as I understand refers to memeory location) remain unchanged.
So in the case of the integer, it was (effectively) passed by value and the other data structure were (effectively) passed by reference. I tried changing the list , the number and the dictionary to just test if the data-structures were changed in place. The number was not bu the list and the dictionary were.
I use the word effectively above, since the 'call-by-object' style of argument passing seems to behave both ways depending on the data-structure passed in the above code
For more complicated data structures, (say numpy arrays etc), is there any quick rule of thumb to recognize which arguments will be passed by reference and which ones passed by value?
Others have already posted good answers. One more thing that I think will help:
evaluates
expr
and bindsx
to the result. On the other hand:does something to
x
and hence can change it (resulting in the same underlying object having a different value).The funny cases come in with things like:
which translate into either
x = x + expr
(rebinding) orx.__iadd__(expr)
(modifying), sometimes in very peculiar ways:(so
x
was rebound, since integers are immutable)Here
x[0]
, which is itself mutable, was mutated in-place; but then Python also attempted to mutatex
itself (as withx.__iadd__
), which errored-out because tuples are immutable. But by thenx[0]
was already mutated!Numbers, strings, and tuples in Python are immutable; using augmented assignment will rebind the name.
Your other types are merely mutated, and remain the same object.
The key difference is that in C-style language, a variable is a box in memory in which you put stuff. In Python, a variable is a name.
Python is neither call-by-reference nor call-by-value. It's something much more sensible! (In fact, I learned Python before I learned the more common languages, so call-by-value and call-by-reference seem very strange to me.)
In Python, there are things and there are names. Lists, integers, strings, and custom objects are all things.
x
,y
, andz
are names. Writingmeans "construct a new thing
[]
and give it the namex
". Writingmeans "construct a new thing
[]
with namex
, construct a new function (which is another thing) with namefoo
, and callfoo
on the thing with namex
". Nowfoo
just appendsNone
to whatever it received, so this reduces to "appendNone
to the the empty list". Writingmeans "construct a new thing
0
with namex
, construct a new functionfoo
, and callfoo
onx
". Insidefoo
, the assignment just says "renamex
to 1 plus what it used to be", but that doesn't change the thing 0.