This question already has answers here:
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
How do I pass a variable by reference?
(39 answers)
Reassigning variables in python [duplicate]
(1 answer)
Closed 1 year ago.
So, here is my snippet. Here when I pass my original list (temp) to add_more. temp get updated.
temp = [1, 2, 3]
def add_more(l):
l.append(5)
add_more(temp)
print(temp)
But here when I do this:
temp = [1, 2, 3]
def add_more(l):
l = []
add_more(temp)
print(temp)
but here temp remain same as [1, 2, 3] and is not an empty list why?
In the first example, you manipulate the list you have passed to the function. but, in the second one you assign an empty list to the label "l", as a result, the list you have passed to the function, does not change.
In the first example, you have passed the reference of temp in the function. The .append method then adds the new element to it. append mutates the list and the reference of the list passed is mutated
In the second example, the parameter l is over written by the assignment expression l = []creating a variable l local to the function only. So temp remains unchanged.
This is related to How do I pass a variable by reference?
In short, list as a parameter is mutable.
In your first snippet, l points to what temp points to, and thus modification of l is also performed for temp.
In your second snippet, l = [] points l to this new empty list, while temp still points to the original one.
Hope this answers your question!
Because append is a mutable operation, and when the code inside the function l.append(5) is executed, it mutates the list l, in second case, when you are l=[] that's creation of an empty list, not the modification of the list passed.
Moreover, you can use id to verify it
First approach
temp = [1, 2, 3]
def add_more(l):
print(f'id of l:{id(l)} before')
l.append(5)
print(f'id of l:{id(l)} after')
add_more(temp)
print(f'id of temp: {id(temp)}')
id of l:1605604079816 before
id of l:1605604079816 after
id of temp: 1605604079816
Second approach
temp = [1, 2, 3]
def add_more(l):
print(f'id of l:{id(l)} before')
l = []
print(f'id of l:{id(l)} after')
add_more(temp)
print(f'id of temp: {id(temp)}')
id of l:1605677713736 before
id of l:1605604079816 after
id of temp: 1605677713736
In the first approach id, that is essentially the memory representation of a variable, is same for temp and before and after inside the function.
But in second case, id is same for temp and before in the function, but not after because you are just creating an empty list and assigning it to l, which is just a variable local inside the function.
l is not the list. It is a reference to that list.
If you want to change the value of the list in a function (rather than the value of the parameter that refers to the list), you must use slicing:
temp = [1, 2, 3]
def add_more(l):
l[:] = []
add_more(temp)
print(temp)
#[]
Related
Let suppose I have a list l and I make new list temp from l. When I try to delete element from temp it also deletes element from l too. This is so strange. How can I only delete element from temp?
l = [1,2,3]
temp = l
temp.remove(3)
print(l, temp)
Output: ([1, 2], [1, 2])
Actually both l and temp are names that refer to the same object list in memory. When you do something with any of them, you are doing it to the underlying object list that is pointed by those two names.
You can always create a new object by using the copy method:
temp = l.copy()
And that way, you will be having two names pointing to different objects.
When you create a list using temp=l, python doesn't create new object.
It refers to the same object as temp so whatever changes you make will reflect in original list as well. Use copy method to avoid this
l = [1,2,3]
temp = l.copy()
temp.remove(3)
print(l, temp)
Output:
[1, 2, 3] [1, 2]
If you do this:
l = [1,2,3]
temp = l
You haven't created a new list, but instead you have merely created a new reference to the same list. Changing the list through either variable changes it for both.
To copy the list, you can do:
temp = list(l)
Another way is to do temp = l[:], which is equivalent. In Python 3 you can also do temp = l.copy(), but that won't work in Python 2.
Then you can change either list without affecting the other.
temp = l is simply assigning temp to a reference of l. So whatever you do to one list, the other is affected.
We can check this with the id() function:
print(id(l))
# 1739514774400
print(id(temp))
# 1739514774400
Which returns the same unique address for each object in memory.
We can see this in the documentation:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.
In this case both l and temp are the same, so they have the same identities.
We can fix this by assigning a copy instead:
l = [1,2,3]
temp = l[:]
Which now gives different identities and only removes 2 from temp:
print(id(l))
# 1822815159168
print(id(temp))
# 1822815159104
temp.remove(3)
print(l, temp)
# [1, 2, 3] [1, 2]
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 3 years ago.
I tried solving this problem on hackerrank where you are given a list a and an integer d', you should rotate the list a to left d times and return the rotated list.
But, when I used this python code for a =[1,2,3,4,5] and d = 4 I got the output as [1,1,1,1,1] instead of [5,1,2,3,4].
temp = a
for j in range(len(a)):
a[j-d] = temp[j]
return a
But when I explicitly copied each element of list 'a' into the list 'temp' it worked fine and I passed all the test cases.
temp = []
for i in range(len(a)):
temp.append(a[i])
for j in range(len(a)):
a[j-d]=temp[j]
return a
Can somebody explain what was wrong with the earlier code?? Thank you!
temp = a
This is a reference assignment. You merely created another reference pointing to the same underlying list object.
temp = a[:]
This will create a new object and assign temp to the new object. (So does your loop of explicitly copying)
This is because list is a mutable type in python - so new objects are not created upon assignment.
You set temp = a which indicate that they are referring to the same pointer
a simple check you could do is to check their memory address
id(temp)
id(a)
You will notice both temp and a referring to the same address therefore changes done on a will also affect temp and vice versa.
In your second method, you created a new list instead of assigning it using an existing list, therefore the address are not similar and they are not referencing one another.
itertools.cycle is your friend for this:
import itertools
def rotate(l, n):
it = itertools.cycle(l)
for _ in range(n):
next(it)
return [next(it) for _ in range(len(l))]
print(rotate([1,2,3,4,5], 4))
result:
[5, 1, 2, 3, 4]
Here you have the live example
a =[1,2,3,4,5]
d = 4
for i in range(0,4):
# repeat the below steps for d times
temp=a[0] # store the a[0] in temp
for j in range(0,len(a)-1):
a[j]=a[j+1] # move all elements located in 1 to len(a)-1 to its previous location
a[len(a)-1]=temp # store the temp in a[len(a)-1]
print(a)
output:
[5, 1, 2, 3, 4]
This is just a question asking for the difference in the code.
I have several lists ie. a=[], b=[], c=[], d=[]
Say if I have a code that appends to each list, and I want to reset all these lists to its original empty state, I created a function:
def reset_list():
del a[:]
del b[:]
del c[:]
del d[:]
So whenever I call reset_list() in a code, it removes all the appended items and set all lists to []. However, the one below doesn't work:
def reset_list():
a = []
b = []
c = []
d = []
This might be a stupid question but I was wondering why the second one wouldn't work.
When you do del a[:] then it looks for the variable a (including outer contexts) and then performs del found_a[:] on it.
But when you use a = [] it creates a name a in the current context and assigns an empty list to it. When the function exits the variable a from the function is not "accessible" anymore (destroyed).
So in short the first works because you change the a from an outer context, the second does not work because you don't modify the a from the outer context, you just create a new a name and temporarily (for the duration of the function) assigns an empty list to it.
There's a difference between del a[:] and a = []
Note that these actually do something different which becomes apparent if you have additional references (aliases) to the original list. (as noted by #juanpa.arrivillaga in the comments)
del list[:] deletes all elements in the list but doesn't create a new list, so the aliases are updated as well:
>>> list_1 = [1,2,3]
>>> alias_1 = list_1
>>> del alist_1[:]
>>> list_1
[]
>>> alias_1
[]
However a = [] creates a new list and assigns that to a:
>>> list_2 = [1,2,3]
>>> alias_2 = list_2
>>> list_2 = []
>>> list_2
[]
>>> alias_2
[1, 2, 3]
If you want a more extensive discussion about names and references in Python I can highly recommend Ned Batchelders blog post on "Facts and myths about Python names and values".
A better solution?
In most cases where you have multiple variables that belong together I would use a class for them. Then instead of reset you could simply create a new instance and work on that:
class FourLists:
def __init__(self):
self.a = []
self.b = []
self.c = []
self.d = []
Then you can create a new instance and work with the attributes of that instance:
>>> state = FourLists()
>>> state.a
[]
>>> state.b.append(10)
>>> state.b.extend([1,2,3])
>>> state.b
[10, 1, 2, 3]
Then if you want to reset the state you could simply create a new instance:
>>> new_state = FourLists()
>>> new_state.b
[]
You need to declare a,b,c,d as global if you want python to use the globally defined 'versions' of your variables. Otherwise, as pointed out in other answers, it will simply declare new local-scope 'versions'.
a = [1,2,3]
b = [1,2,3]
c = [1,2,3]
d = [1,2,3]
def reset_list():
global a,b,c,d
a = []
b = []
c = []
d = []
print(a,b,c,d)
reset_list()
print(a,b,c,d)
Outputs:
[1, 2, 3] [1, 2, 3] [1, 2, 3] [1, 2, 3]
[] [] [] []
As pointed out by #juanpa.arrivillaga, there is a difference between del a[:] and a = []. See this answer.
The 1st method works because:
reset_list() simply deletes the contents of the four lists. It works on the lists that you define outside the function, provided they are named the same. If you had a different name, you'd get an error:
e = [1,2,3,4]
def reset_list():
del a[:] #different name for list
NameError: name 'e' is not defined
The function will only have an effect if you initialize the lists before the function call. This is because you are not returning the lists back after the function call ends:
a = [1,2,3,4] #initialize before function definition
def reset_list():
del a[:]
reset_list() #function call to modify a
print(a)
#[]
By itself the function does not return anything:
print(reset_list())
#None
The 2nd method doesn't work because:
the reset_list() function creates 4 empty lists that are not pointing to the lists that may have been defined outside the function. Whatever happens inside the function stays inside(also called scope) and ends there unless you return the lists back at the end of the function call. The lists will be modified and returned only when the function is called. Make sure that you specify the arguments in reset_list(a,..) in the function definition:
#function definition
def reset_list(a):
a = []
return a
#initialize list after function call
a = [1,2,3,4]
print("Before function call:{}".format(a))
new_a = reset_list(a)
print("After function call:{}".format(new_a))
#Output:
Before function call:[1, 2, 3, 4]
After function call:[]
As you've seen, you should always return from a function to make sure that your function "does some work" on the lists and returns the result in the end.
The second function (with a = [ ] and so on) initialises 4 new lists with a local scope (within the function). It is not the same as deleting the contents of the list.
def mutation(input_list):
list_copy = input_list[:]
list_copy[0] = 10
input_list = list_copy
# Correctly mutates
sample_list = [0,1,2]
sample_copy = sample_list[:]
sample_copy[0] = 10
sample_list = sample_copy
print(sample_list)
# Incorrectly mutates
sample_list = [0,1,2]
mutation(sample_list)
print(sample_list)
In the top snippet of code, I've made a copy of a list and modified it. I then set the original to the copy and then it works.
What confuses me is why doing this process outside of a function works but if I were to do it inside a function (the 2nd snippet of code), it fails?
For reference, the code returns:
[10, 1, 2]
[0, 1, 2]
EDIT: I know that calling input_list[0] = 10 works. I just want to know what makes this different from what I showed above all in memory?
In mutation, input_list starts out pointing at the same object as sample_list, but later you make it point at list_copy.
sample_list is not modified. It is still pointing at the original object.
When you do it outside of the function you change sample_list to point to the new object before printing it.
I think that using the built-in function id to show the object ID will help here. If the ID of two variable names gives the same result then they refer to the same object; otherwise the objects are different.
>>> def mutation(input_list):
... print(id(input_list))
... list_copy = input_list[:]
... print(id(list_copy))
... input_list = list_copy
... print(id(input_list))
...
>>> a = list(range(10))
>>> print(id(a))
140737233394376
>>> mutation(a)
140737233394376
140737233289160
140737233289160
In the above, we see that after input_list = list_copy, the name input_list refers to identically the same object in memory as list_copy, which means it no longer refers to the list given as the function argument. This is why the mutation you expect does not work - you are modifying an entirely different object.
That's because you sets new value for input_list which is local variable of mutation function.
The simplest solution is changing value of first element of list passed as argument:
def mutation(input_list):
input_list[0] = 10
Otherwise you can write function which changes value of global variable called sample_list
def mutation():
global sample_list
list_copy = sample_list[:]
list_copy[0] = 10
sample_list = list_copy
I am implementing few list methods manually like append(), insert(), etc. I was trying to add element at the end of list (like append method). This the working code i am using:
arr = [4,5,6]
def push(x, item):
x += [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6,7]
But when I am implementing same code with little difference. I am getting different output.
arr = [4,5,6]
def push(x, item):
x = x + [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6]
And I am facing same for insert method. Here is code for insert method:
arr = [4,5,7,8]
def insert(x, index, item):
x = x[:index] + [item] + x[index:]
return x
insert(arr,2,6)
print(arr) #Output: [4,5,7,8]
I know I can store return value to the list by arr=insert(arr,2,6) but I want an alternative solution, that list automatically gets update after calling function like in my first code sample.
Edit 1:
I think x[index:index] = [item] is better solution for the problem.
x += [item] and x = x + [item] are not a little difference. In the first case, you are asking to make a change to the list referenced by x; this is why the result reflects the change. In the second, you are asking to have x reference a new list, the one made by combining x's original value and [item]. Note that this does not change x, which is why your result is unchanged.
Also note that your return statements are irrelevant, since the values being returned are ignored.
In your first example you mutated(a.k.a changed) the list object referred to by x. When Python sees x += [item] it translates it to:
x.__iadd__([item])
As you can see, we are mutating the list object referred to by x by calling it's overloaded in-place operator function __iadd__. As already said, __iadd__() mutates the existing list object:
>>> lst = [1, 2]
>>> lst.__iadd__([3])
[1, 2, 3]
>>> lst
[1, 2, 3]
>>>
In your second example, you asked Python to assign x to a new reference. The referenced now referrer to a new list object made by combining (not mutating) the x and [item] lists. Thus, x was never changed.
When Python sees x = x + [item] it can be translated to:
x = x.__add__([item])
The __add__ function of lists does not mutate the existing list object. Rather, it returns a new-list made by combing the value of the existing list and the argument passed into __add__():
>>> lst = [1, 2]
>>> lst.__add__([3]) # lst is not changed. A new list is returned.
[1, 2, 3]
>>>
You need to return the the result of the version of push to the arr list. The same goes for insert.
You can assign to a slice of the list to implement your insert w/o using list.insert:
def insert(x, index, item):
x[:] = x[:index] + [item] + x[index:]
this replaces the contents of the object referenced by x with the new list. No need to then return it since it is performed in-place.
The problem is that you haven't captured the result you return. Some operations (such as +=) modify the original list in place; others (such as x = x + item) evaluate a new list and reassign the local variable x.
In particular, note that x is not bound to arr; x is merely a local variable. To get the returned value into arr, you have to assign it:
arr = push(arr, 7)
or
arr = insert(arr, 2, 6)
class DerivedList(list):
def insertAtLastLocation(self,obj):
self.__iadd__([obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6)
print(lst) #output[1,1,1,5,6]
You can use this code to add one element at last position of list
class DerivedList(list):
def insertAtLastLocation(self,*obj):
self.__iadd__([*obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6,7)
print(lst) #output[1,1,1,5,6,7]
lst.insertAtLastLocation(6,7,8,9,10)
print(lst) #output[1,1,1,5,6,7,8,9,10]
This code can add multiple items at last location