Question

This may be for most languages in general, but I'm not sure. I'm a beginner at Python and have always worked on copies of lists in C# and VB. But in Python whenever I pass a list as an argument and enumerate through using a "for i in range," and then change the value of the list argument, the input values actually changes the original list. I thought Python was supposed to pass arguments by value by default so that once the function is finished I still have the original values from before I called the function. What am I missing? Thanks!

Was it helpful?

Solution

Python does pass arguments by value but the value you are receiving is a copy of the reference (incidentally this is the exact same way that C#, VB.NET, and Java behave as well).

This is the important thing to remember:

Objects are not passed by reference - object references are passed by value.

Since you have a copy of the reference, any operation on what that reference points to will be just as if you were holding the original reference itself.

OTHER TIPS

Python -- just like Java does for anything but primitive scalars, and like C# and VB.NET do for the default kind parameters as opposed to boxed types and out / ref parms -- passes "by object reference" (search for that phrase here -- it's how Guido, Python's architect and creator, uses to explain this argument-passing concept).

Every name is a reference to some object; passing a name (or any other expression) as an argument is just creating yet another reference to the same object (which the function body can access through the parameter's name). ((There's no such thing as "a reference to a name": there are names, which are one kind of reference to objects, and object -- period)).

When you're passing a mutable object, i.e. one which has mutating methods (like for example a list), the called function can mutate the object by calling, directly or indirectly, its mutating methods. ((By "indirectly", I mean "through operators" -- for example:

somelist[len(somelist):] = [whatever]

is exactly identical to somelist.append(whatever).))

When you want to pass a list into a function, but do not want the function to be able to mutate that list in any way, you must pass a copy of the list instead of the original -- just like in Java, C#, VB.NET.

Be very clear about the distinction between rebinding a name and mutating an object. Rebinding the name ("barename", that is -- qualified-names are different;-) only affects that name -- NOT any object whatsoever. For example:

def f1(alist):
  alist = [23]

def f2(alist):
  alist[:] = [23]

Can you spot the difference between these two functions? One is rebinding the barename alist -- without any effect whatsoever on anything. The other is mutating (altering, changing, ...) the list object it received as its argument -- by setting its content to be a one-item list with an int as its sole item. Completely, utterly different things!!!

To add to Andrew's answer, you need to explicitly make a copy of a list if you want to retain the original. You can do this using the copy module, or just do something like

a = [1,2]
b = list(a)

Since copying objects usually implies a performance hit, I find it helpful to explicitly use the copy module in my larger projects. That way, I can easily find all the places where I'm going to use a bunch more memory.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top