Modifying a list inside a function - python

Suppose I have function with list parameter, and inside its body I want to modify passed list, by copying elements of an array to the list:
def function1 (list_arg):
a = function2() #function2 returns an array of numbers
list_arg = list(a)
list1 = [0] * 5
function1(list1)
list1
[0,0,0,0,0]
When doing it like this, it doesn't work. After executing function1(list1), list1 remains unchanged. So, how to make function1 return list1 with the same elements (numbers) as array a?

If you assign something to the variable list_arg, it will from then on point to the new value. The value it pointed to before that assignment (your original list) will stay unchanged.
If you, instead, assign something to elements of that list, this will change the original list:
list_arg[:] = list(a)
This will make your code work as you wanted it.
But keep in mind that in-place changes are hard to understand and probably can confuse the next developer who has to maintain your code.

What I think you are asking is why after calling f(a), when f re-assigns the a you passed, a is still the "old" a you passed.
The reason for this is how Python treats variables and pass them to functions. They are passed by reference, but the reference is passed by value (meaning that a copy is created). This means that the reference you have inside f is actually a copy of the reference you passed. This again implies that if you reassign the variable inside the function. It is a local variable existing only inside the function; re-assigning it won't change anything in outside scopes.
Now, if you rather than reassigning the local variable/reference inside f (which won't work, since it's a copy) perform mutable operations on it, such as append(), the list you pass will have changed after f is done.
See also the question How do I pass a variable by reference? which treats the problem and possible solutions in further detail.
TL;DR: Reassigning a variable inside a function won't change the variable you passed as an argument outside the function. Performing mutable operations on the variable, however, will change it.

You can operate on the list to change its values (eg, append something to it, or set its values) but changes will be reflected outside of the function only if you operate on the reference to the passed in object:
def function1 (list_arg):
list_arg.append(5)
If you have questions when doing this, print out the ids:
def function1 (list_arg):
print 1, id(list_arg)
list_arg[:] = ["a", "b", "c"]
print 2, id(list_arg)
list_arg = range(10)
print 3, id(list_arg)
x = [1,2,3]
function1(x)
print x
prints:
1 4348413856
2 4348413856
3 4348411984
['a', 'b', 'c']
That is, x is changed in place, but assigning to the function's local variable list_arg has no impact on x, because is then just assigns a different object to list_arg.

You're changing a reference to a local variable. When you pass in list_arg this way:
def function1 (list_arg):
list_arg is a reference to an underlying list object. When you do this:
list_arg = list(a)
You're changing what list_arg means within the function. Since the function exits right after that, list_arg = list(a) has no effect.
If you want to actually change the reference to the list you have to do assign it to the result of the function.
def function1 ():
'a = some array'
return list(a)
list1 = [0] * 5
list1 = function1()
Or you could modify the contents of the list without changing the reference.
def function1(list_arg):
del list_arg[:] # Clears the array
'a = some array'
list_arg.extend(a)

Related

List assignment (by value) vs list append in recursive function [duplicate]

Suppose I have function with list parameter, and inside its body I want to modify passed list, by copying elements of an array to the list:
def function1 (list_arg):
a = function2() #function2 returns an array of numbers
list_arg = list(a)
list1 = [0] * 5
function1(list1)
list1
[0,0,0,0,0]
When doing it like this, it doesn't work. After executing function1(list1), list1 remains unchanged. So, how to make function1 return list1 with the same elements (numbers) as array a?
If you assign something to the variable list_arg, it will from then on point to the new value. The value it pointed to before that assignment (your original list) will stay unchanged.
If you, instead, assign something to elements of that list, this will change the original list:
list_arg[:] = list(a)
This will make your code work as you wanted it.
But keep in mind that in-place changes are hard to understand and probably can confuse the next developer who has to maintain your code.
What I think you are asking is why after calling f(a), when f re-assigns the a you passed, a is still the "old" a you passed.
The reason for this is how Python treats variables and pass them to functions. They are passed by reference, but the reference is passed by value (meaning that a copy is created). This means that the reference you have inside f is actually a copy of the reference you passed. This again implies that if you reassign the variable inside the function. It is a local variable existing only inside the function; re-assigning it won't change anything in outside scopes.
Now, if you rather than reassigning the local variable/reference inside f (which won't work, since it's a copy) perform mutable operations on it, such as append(), the list you pass will have changed after f is done.
See also the question How do I pass a variable by reference? which treats the problem and possible solutions in further detail.
TL;DR: Reassigning a variable inside a function won't change the variable you passed as an argument outside the function. Performing mutable operations on the variable, however, will change it.
You can operate on the list to change its values (eg, append something to it, or set its values) but changes will be reflected outside of the function only if you operate on the reference to the passed in object:
def function1 (list_arg):
list_arg.append(5)
If you have questions when doing this, print out the ids:
def function1 (list_arg):
print 1, id(list_arg)
list_arg[:] = ["a", "b", "c"]
print 2, id(list_arg)
list_arg = range(10)
print 3, id(list_arg)
x = [1,2,3]
function1(x)
print x
prints:
1 4348413856
2 4348413856
3 4348411984
['a', 'b', 'c']
That is, x is changed in place, but assigning to the function's local variable list_arg has no impact on x, because is then just assigns a different object to list_arg.
You're changing a reference to a local variable. When you pass in list_arg this way:
def function1 (list_arg):
list_arg is a reference to an underlying list object. When you do this:
list_arg = list(a)
You're changing what list_arg means within the function. Since the function exits right after that, list_arg = list(a) has no effect.
If you want to actually change the reference to the list you have to do assign it to the result of the function.
def function1 ():
'a = some array'
return list(a)
list1 = [0] * 5
list1 = function1()
Or you could modify the contents of the list without changing the reference.
def function1(list_arg):
del list_arg[:] # Clears the array
'a = some array'
list_arg.extend(a)

How do I modify the contents of a list I pass as a variable length argument?

I think that the variable length argument turns into a tuple when it enters the edit_list function, so I changed it from a Tuple to a List to edit it. When it returns, I assume it is still treated as a Tuple and therefore no changes to the argument values can be returned?
If so, how would I go about editing the contents of a list that is used in a variable length argument context?
def main():
x = ['hi','hello','world',1,2]
edit_list(*x)
print(x)
#why can't I change the list index 1 value to '2' and then
#return the modified arg list to main and then print it out?
def edit_list(*args):
args = list(args)
print(args)
args[1] = 2
return args
if __name__ == '__main__' : main()
You would need to pass in the list directly, instead of unpacking the list using edit_list(*x).
def edit_list(my_list):
my_list[1] = 2
def main():
x = [ ... ]
edit_list(x)
print(x)
To understand the mechanism of this, you should try to be familiar with the concept "mutable" and "immutable". (of course, only if you want to be better at Python).
Take your code as an example, if the element you passed in is a list, and you are changing the element of that list, you will get a different result.
def main():
x = ['hi',['hello'],'world',1,2]
edit_list(*x)
print(x)
# After you run edit_list, the original x will be changed
def edit_list(*args):
args = list(args)
print(args)
args[1][0] = 2
return args
In python, objects are either "mutable" or "immutable". lists for example, are mutable. integers, strings are immutable. When you pass a mutable object to a function, you pass in it's reference so when you edit it(not assigning another object to the variable) you will edit the original object. However, if it's immutable, when you pass in the object to the function, it will generate a copy.
Also use your code as an example. You are effectively doing
edit_list("hi", "hello", "world", 1, 2)
All the arguments are immutable, so you copied each of them and give them to the function. Therefore, when you are inside the function, you already have different objects to the original. There's nothing you can do to change the original list.
However, if you pass them in as a list
edit_list(x)
Because x is a mutable object, you pass the reference, or the original object in the function. So when you edit the list lst[1] = 2, the original one will change. However, if you do something like lst = [1, 2, 3], the original x won't be changed, because you just created another object and assigned to the unrelated variable lst.

Local and Global lists in Python

Please help me as I am new to python
when I call this function the original value of list1 changes
def mystery(list1):
list1[0] , list1[1] = list1[1], list1[0]
list1 = [7,82,44,23,11]
mystery(list1)
print(list1) #prints [82, 7, 44, 23, 11]
how it can change the value of global list1
if I change my function to
def mystery(list1):
list1 = list1 + list1[2:5]
then I am getting the global value of list1 and not the updated one.
lists are mutable objects in python, so if you are passing to a function a list all the changes that you make to the list inside your function will be reflected everywhere/globally
If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.
If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.[more details here]
The reason list1 is changing is because you're actually modifying the list object inside of the function.
It really has nothing todo with global / local variables, if you renamed the parameter inside your function to something else, and still passed in list1, list1 would be modified.
If you're wanting to return a new list, you need to first create a copy of the list, there's many ways to do this. list(list1) is one way. Then return the list at the end of the function.
If I understand your queston, you want to actually append some more values to the passed in list, use list1.append(...) to add to the end of the list.
And since you're modifying the list itself, it'll change in the larger scope.
But it's still not using the global scope, as you're just using a var with the same name in the local scope.
Maybe just return the list?
def mystery(list1):
return list1 + list1[2:5]
list1 = [7,82,44,23,11]
list1 = mystery(list1)
print(list1)
In python, assigning a value to a variable like a = 5 means you are creating an object in the memory for the value 5. Now a is a link to that object holding 5 (in Layman's terms). If you change a now, say a = "something else", this creates a new object for the string "something else" in somewhere else in the memory and now a is pointing to that. This is why you get to change the data type of python variables.
When you pass something to a python function, that link is the one that is passed on. So when you call mystery(list1), original link to the list1 is passed. Therefore changing an element in the list1 means you are assigning a new value to that particular element in the original list1. No matter you are inside or outside of the function mystery() in this case since you will be using the original link you created to access the element inside list1 to change it. The change happens inside the list1; list1 didn't get reassigned.
However when you do list1 = "something new" inside your function mystery(), you are creating a new variable list1 inside the function which is local to the function. You are not changing the original list1.
Passing mutable objects into a function and then modifying the object inside the function will have the same effect as modifying the object directly.
list1 = [1,2,3]
def func(list1):
list1[0] = 5
>>>list1
[5,2,3]
This is effectively same as directly running list1[0] = 5
However if you pass an immutable object into a function such as tuple, It will not support item assignement TypeError: 'tuple' object does not support item assignment. So you need to build a new immutable object and return it.
>>> tup1 = (1,2,3) # doing tup1[0] = 5 will cause TypeError
>>> tup2 = tup1 + (5,)
>>> tup2
(1, 2, 3, 5)
Put it in function,
>>> def func2(tup1):
return tup1 + (5,)
>>> func2(tup1=(1,2,3))
(1, 2, 3, 5)

Why does a function allow a mutation of a global variable without returning it?

I have a question about variable scope in Python. Why is a mutation of a variable allowed without returning the mutated variable?
def mutation(L):
L.append("x")
L = []
mutation(L)
print(L)
I would expect this to print [], as the mutation of L inside the function only affects the local scope. Why is "L" mutated even in the global scope?
Since Python is pass by object reference, when you pass something to a function it points to the same object in memory that you can manipulate. If you want a function to not modify the original list, you need to make a copy of it when passing it to the function or inside of the function itself.
Sending a copy to the function:
def mutation(L):
L.append("x")
L = []
mutation(list(L))
print(L)
# Prints out []
Making a copy inside the function:
def mutation(L):
L = list(L)
L.append("x")
L = []
mutation(L)
print(L)
# Prints out []
This is because you are actually changing the original list/object passed into the function. Note that the underlying principle is pass-by-object-reference. So changes to the passed parameter within the function will reflect outside.
If you don't want to change globally, use [:] to create a copy and this would perform operations on the copy without mutating the original list.
def mutation(L):
L = L[:]
L.append("x")
L = []
mutation(L)
print(L)
# []

Changing argument value using function?

It looks like I can only change the value of mutable variables using a function, but is it possible to change immutable
Code
def f(a, b):
a += 1
b.append('hi')
x = 1
y = ['hello']
f(x, y)
print(x, y) #x didn't change, but y did
Result
1 [10, 1]
So, my question is that is it possible to modify immutable variables using functions? If no then why? What's the reason that python bans people from doing that?
In python, the list is passed by object-reference. Actually, everything in python is an object but when you pass a single variable to function it creates a local copy of that if a value is changed but in case of a list if it creates a local copy even than the reference remains to the previous list object. Hence the value of the list will not get changed.\
You can refer to the link.
You can check the following example for clarification.
def fun1(b):
for i in range(0,len(b)):
b[i]+=4
arr=[1,2,3,4]
print("Before Passing",arr)
fun2(arr)
print("After Passing",arr)
#output
#Before Passing [1, 2, 3, 4]
#After Passing [5, 6, 7, 8]
If you do not want any function to change value accidentally you can use an immutable object such as a tuple.
Edit: (Copy example)
We can check it by printing the id of both objects.
def fun(a):
a=5
print(hex(id(a)))
a=3
print(hex(id(a)))
fun(a)
# Output:
# 0x555eb8890cc0
# 0x555eb8890d00
But if we do it with a List object:
def fun(a):
a.append(5)
print(hex(id(a)))
a=[1,2,3]
print(hex(id(a)))
fun(a)
# Output:
# 0x7f97e1589308
# 0x7f97e1589308
Y is not value its just some bindings to memory. When You pass it to function its memory address is passed to function (call by reference). On the other hand x is value and when you pass it to function new local variable is created with same value. (At the assembly level all parameters of function are passed via stack pointer. Value of x and adress of y are pushed to stack pointer.

Categories