Why Can a Function Modify Some Arguments as Perceived by the Caller, But Not Others

Why can a function modify some arguments as perceived by the caller, but not others?

Some answers contain the word "copy" in a context of a function call. I find it confusing.

Python doesn't copy objects you pass during a function call ever.

Function parameters are names. When you call a function Python binds these parameters to whatever objects you pass (via names in a caller scope).

Objects can be mutable (like lists) or immutable (like integers, strings in Python). Mutable object you can change. You can't change a name, you just can bind it to another object.

Your example is not about scopes or namespaces, it is about naming and binding and mutability of an object in Python.

def f(n, x): # these `n`, `x` have nothing to do with `n` and `x` from main()
n = 2 # put `n` label on `2` balloon
x.append(4) # call `append` method of whatever object `x` is referring to.
print('In f():', n, x)
x = [] # put `x` label on `[]` ballon
# x = [] has no effect on the original list that is passed into the function

Here are nice pictures on the difference between variables in other languages and names in Python.

Why does Python sometimes not create new objects for function arguments?

"I assumed that whenever you passed an object into a function, it automatically created a separate copy for you that you could modify without changing the original."

Well, your assumption there is wrong. Variables are passed by reference, not value. This includes if you set a default value for an argument to an empty list, and change it in the function, the next time you try to use that default value, it won't be an empty list anymore.

def foo(bar=[]):
bar.append("baz")

foo()
foo()
# bar is now ["baz", "baz"]

Modifying a variable passed as an argument inside a function

If you did the following it would work, however you will have to change your function signature slightly:

def inp(variableName, prompt='', inttype="str"):
operation = "global {variable}; {variable} = {cast}(input(prompt))"
exec(operation.format(variable=variableName, cast=inttype, prompt=prompt))

So you would be required to modify the method to accept strings as opposed to the actual values.

Therefore if you wanted to modify the variable x you will be required to pass in the string "x"

This sort of wraps exec within a function.

In [19]:     def inp(variableName, prompt='', inttype="str"):
...: operation = "global {variable}; {variable} = {cast}(input(prompt))"
...: exec(operation.format(variable=variableName, cast=inttype, prompt=prompt))
...:

In [20]: inp("x", prompt="hello world", inttype="str")
hello worldbye!

In [21]: print(x)
bye!

Lesson is that python is a dynamic language and anything is possible :) (however I wouldn't recommend this)

List changed in a function

the problem is that when you're doing origin = alist you're actually creating a pointer to the same object, you're not creating a copy of the list.
In order to create a copy of the list I suggest you use the following notation:

origin = alist[:]

By doing that you're creating another object with the same elements inside so that's a copy!

A function unwantedly modifying a list in a python 3.8.6

List b, the one that needs to be displayed with the question mark, has its contents copied into a new variable, and that variable is modified with the question mark

This is where the problem occurs. bModif = b doesn't copy b content into bModif. bModif is a reference to the same list that b refers to. If you want a copy of a list in python, use bModif = b.copy()

Function in Python is treating list like a global variable. How to fix this?

You should read this question at first. why can a function modified some arguments while not others.

Let me rewrite your code for clarification.

def find(li, el):
# li is a list, el is an integer
# do something using li and el

if __name__ == "__main__":
l = [1,2,3,4]
e = 2
find(l, e)

The function find received two objects as parameters, one is li and the other is el. In main, we defined two objects, a list, we called it l, and an integer, we called it e. Then these two objects was passed to find. It should be clear that it is these two objects that passed to the function, not the name. Then your find function has access to this object, called l in main, while called li in find. So when you change li in find, l changed as well.

Hope that answers your question. And to fix this, check deepcopy.



Related Topics



Leave a reply



Submit