Understanding Python's call-by-object style of passing function arguments

function, python

Solution

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`, and `z` are names. Writing

x = []

means "construct a new thing `[]` and give it the name `x`". Writing

x = []
foo = lambda x: x.append(None)
foo(x)

means "construct a new thing `[]` with name `x`, construct a new function (which is another thing) with name `foo`, and call `foo` on the thing with name `x`". Now `foo` just appends `None` to whatever it received, so this reduces to "append `None` to the the empty list". Writing

x = 0
def foo(x):
    x += 1
foo(x)

means "construct a new thing `0` with name `x`, construct a new function `foo`, and call `foo` on `x`". Inside `foo`, the assignment just says "rename `x` to 1 plus what it used to be", but that doesn't change the thing 0.

Problem

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?

Original source

Related problems