This question already has answers here:
Why does += behave unexpectedly on lists?
(9 answers)
What exactly does += do?
(17 answers)
Closed 1 year ago.
Why it gives different outputs:
is there a difference between + and +=
def func1(lst1, lst2):
lst1 += lst2
lst1 = [1, 2, 3]
lst2 = ["a", "b"]
func1(lst1, lst2)
print(lst1)
and
def func1(lst1, lst2):
lst1 = lst1 + lst2
lst1 = [1, 2, 3]
lst2 = ["a", "b"]
func1(lst1, lst2)
print(lst1)
Thanks in advance
The difference here is that += changes the initial list lst1. Whilst the lst1 = lst1 + lst2 creates a new list and rewrites local reference lst1 which does not affect global object lst1. If you try printing lst1 inside the functions, both cases would give you the same result.
A good IDE will give you a hint, like so:
If you recognize the dark grey color of lst1, move your mouse over it and it will give you a hint. Here, the problem is that the local variable is not used, as explained by #Yevhen Kuzmovych.
+= can have a different implementation than +. That's the case for lists: += modifies the list inplace whereas + gives you a new list and using the same name on the left side makes that not so obvious.
It also says that you have a naming issue, called shadowing. Did you expect lst1 from the outer scope to update, because it has the same name? If you thought that, you need to read about scope. If you know another programming language, you also want to understand the difference between pass by value, pass by reference and pass by assignment (Python).
In your first example, you are passing a reference to lst1. Therefore, the increment affects the list that lst1 refers to.
In your second example, you're also passing a reference but the operation lst1 + lst2 creates a new list instance and lst1 subsequently points at that. Thus your original list is unaffected.
You need to decide if you really want lst1 modified or whether you want to know what the addition of the two lists looks like
Related
Let's say I have a nested list like this
list_1 = [1, 2, 3]
list_2 = ["a", "b", "c"]
li = [list_1, list_2]
How do I clear each item in li so that list_1 and list_2 also become empty. I'm expecting something like:
print(li) # Output: [[],[]]
print(list_1) # Output: []
print(list_2) # Output: []
I tried
for i in range(len(parameters)):
parameters[i] = []
but it doesn't clear list_1 and list_2
You can use the list.clear method to empty the lists in-place and have that effect the original variables. By only assigning empty lists in their place, the original variables are not modified since you are creating new lists:
for p in parameters:
p.clear()
which is equivalent to, but a better and more Pythonic version of:
for i in range(len(parameters)):
parameters[i].clear()
An addition from #ShadowRanger's comment:
As a small addendum, del p[:] is equivalent to p.clear(), and has been supported for longer. At this point, no version of Python without list.clear is supported (the .clear and .copy methods were added in 3.3 for symmetry with set and dict, even though sequences could do the same operations with del seq[:] and seq[:] already), and I agree p.clear() is more self-documenting, but it's worth pointing out that the other approach exists (p[:] = () is also equivalent, but even less explicit than del p[:]), since you'll see it in older code.
I encountered a (in my opinion) extremely strange behavior, when looping through a list of lists. It is very difficult to explain, but here is an example code:
k = [[0],[1],[2]]
for lis in k:
lis_copy = lis
lis_copy.append(0)
print lis
When executing this, I get:
[0, 0]
[1, 0]
[2, 0]
This is very strange for me, as the list which is appended is a copy of lis,
but lis is appended as well. I would assume this list to be untouched.
For example doing the same with a list of integers the following happens:
k = [0,1,2]
for num in k:
num_copy = num
num_copy = 0
print num
Output:
0
1
2
Just as expected num is not touched by the manipulation of num_copy.
If somebody could explain why this is happening and how to avoid this,
like how to disconnect the lis_copy from is, that would be great.
Wow, I am amazed I did not encounter mayor problems before, without knowing this. I think I should review quiet some of my code. Anyway I thought this is somehow connected to the loop, this seems not to be the case, therefore I think the best explanation can be found here:
How to clone or copy a list?
This is because Python lists (and dicts) are not copied to a new list, but the new list becomes a reference to that list. if you truly want to copy the list, use deepcopy
You could use copy.copy() or copy.deepcopy()to avoid this behavior:
import copy
k = [[0],[1],[2]]
for lis in k:
lis_copy = copy.copy(lis)
lis_copy.append(0)
print lis
Output:
[0]
[1]
[2]
Source: https://docs.python.org/2/library/copy.html
Case a:
k = [[0],[1],[2]]
for lis in k:
lis_copy = lis
lis_copy.append(0)
print lis
We have a pointer to a list, and inside the loop we have another pointer made that points to the inner list objects. Then a zero is appended to each object.
Case b:
k = [0,1,2]
for num in k:
num_copy = num
num_copy = 0
print num
We have a pointer to a list, and inside the loop we have another pointer made that points to the inner integers. The difference is that in this case the pointer is changed to then point to a zero object rather than the list elements.
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 7 years ago.
Im a little new to python, so this question may be very easy to answer. However, the logic doesnt make sense to me which is why I am posting this question. While there have been similar questions asked, they all used large chunks of code that were hard to read. Here is the program in python:
list1 = [1,2,3,4,5]
list2 = list1
for i in range(len(list2)):
list2[i] += 1
print list1
When I run this program, it returns list1 as [2,3,4,5,6], which is identical to list2. My question is why list1 is connected to list2, and how to keep them independent of each other. Thank you for answering.
When you do list2 = list1, you are just passing the reference of list1 to list2 , you instead need to make copy of list1 and pass that copy to list2 .
Example -
list1 = [1,2,3,4,5]
list2 = list1[:]
for i in range(len(list2)):
list2[i] += 1
print list1
>> [1, 2, 3, 4, 5]
Or You can also use copy module -
>>> import copy
>>> list1 = [1,2,3,4,5]
>>> list2 = copy.copy(list1)
>>> for i in range(len(list2)):
... list2[i] += 1
...
>>> list1
[1, 2, 3, 4, 5]
If the list contains objects and you may want to copy them as well, in that case use copy.deepcopy()
By default assignment operator in python links the two objects, i.e. creates binding between the two objects.
in order for you to create a new independent object and still copy the original.
you would have to use copy ( shallow or deep , depending on your requirement )
list2 = copy.copy(list1)
This question already has answers here:
Different behaviour for list.__iadd__ and list.__add__
(3 answers)
Closed 9 years ago.
code A:
lst = [1, 2, 3]
for i in range(10):
lst+= ["42"]
code B:
lst = [1, 2, 3]
for i in range(10):
lst = lst + ["42"]
I know the output is the same, but is there a difference in the way the two lists are built?
What's happening in the back actually?
When you do
lst += ["42"]
You are mutating lst and appending "42" at the end of it. But when you say,
lst = lst + ["42"]
You are creating a new list with lst and "42" and assigning the reference of the new list to lst. Try this program to understand this better.
lst = ["1"]
print(id(lst))
lst += ["2"]
print(id(lst))
lst = lst + ["3"]
print(id(lst))
The first two ids will be the same, bu the last one will be different. Because, a new list is created and lst now points to that new list.
Not knowing the difference between these two will create a problem, when you pass a list as a parameter to a function and appending an item to it, inside the function like this
def mutate(myList):
myList = myList + ["2"] # WRONG way of doing the mutation
tList = ["1"]
mutate(tList)
print(tList)
you will still get ['1'], but if you really want to mutate myList, you could have done like this
def mutate(myList):
myList += ["2"] # Or using append function
tList = ["1"]
mutate(tList)
print(tList)
will print ['1', '2']
This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
For quite a bit of time now I have been trying to figure out a way to loop through a list and remove the current item that I'm at. I can't seem to get this working as I would like it to. It loops just 1 time through, but I wanted it to loop 2 times. When I remove the removal line - it loops 2 times.
a = [0, 1]
for i in a:
z = a
print z.remove(i)
The output:
[1]
The output that I was expecting:
[1]
[0]
You're changing the list while iterating over it -- z = a doesn't make a copy, it just points z at the same place a points.
Try
for i in a[:]: # slicing a list makes a copy
print i # remove doesn't return the item so print it here
a.remove(i) # remove the item from the original list
or
while a: # while the list is not empty
print a.pop(0) # remove the first item from the list
If you don't need an explicit loop, you can remove items that match a condition with a list comprehension:
a = [i for i in a if i] # remove all items that evaluate to false
a = [i for i in a if condition(i)] # remove items where the condition is False
It is bad practice modify a list while you're looping through it†. Create a copy of the list:
oldlist = ['a', 'b', 'spam', 'c']
newlist = [x for x in oldlist if x != 'spam']
To modify the original list, write the copy back in-place with a slice assignment:
oldlist[:] = [x for x in oldlist if x != 'spam']
† For a gist of why this might be bad practice, consider the implementation details of what goes on with the iterator over the sequence when the sequence changes during iteration. If you've removed the current item, should the iterator point to the next item in the original list or to the next item in the modified list? What if your decision procedure instead removes the previous (or next) item to the current?
The problem is that you're modifying a with remove so the loop exits because the index is now past the end of it.
Don't try to remove multiple items of a list while looping the list. I think it's a general rule you should follow not only in python but also in other programming languages as well.
You could add the item to be removed into a separate list. And then remove all objects in that new list from the original list.