Let's say I have:
a = 1
b = 2
C = 'r'
my_list = [a,b,c]
Now let's say that a, b and c are unknown and I don't know their names.
If I do:
for x in my_list: del x
it doesn't work. a, b, c have not been deleted.
Can someone explain me why?
As #Coldspeed mentions in his comment, the variable x which you delete is not the same as the element in the list object.
Similar behaviour will be seen if you try to assign to x:
for x in my_list: x='bla' #does not modify anything in my_list
However, as the items are references to the same memory block, the comparison x is my_list[0] will equate to True in the first loop iteration.
As such, it is possible to perform operations on the list through usage of the shared reference, for example:
for x in my_list[:]: my_list.remove(x) #results in an empty list
Care has to be taken to first create a copy of the list and iterate over these items though, as was done in the previous lines. If you are hasty and loop over the items of a dynamically changing list, you will run into some more python magic.
for x in my_list: my_list.remove(x) #the first element gets deleted, then the second element in the list, which now has length 2, is deleted.
#Final result is the list [2] remaining
you have multiple issues here:
1. variable in list
a = 1
b = 2
my_list = [a,b]
assigns the values 1 and 2 to the list, not the vars. You can use mutable objects to get you desire: Immutable vs Mutable types
2. deleting a copy from a listvalue
for x in my_list:
del x
like in 1. x is just the value from the list (e.g. 1, 2, 'c'), but even worse, its a additional reference count to the memory.
Deleting it results in decreasing the counter, not deleting the value from memory, since at least one more counter is given by the original list (and in your case the vars (a,b,c) from the beginning).
More Info: Arguments are passed by assignment
3. deleting while iterating
for x in my_list:
del x
contains an other problem. If you would change the code to mylist.remove(x), to at least remove the entrie from the list, you would also skip every second member of the list. Quick Example:
li = [1,2,3]
for x in li:
li.remove(x)
first iteration would be x = 1. Deleting 1 from li results in li = [2,3]. Then the loop continous with the second position in the list: x=3 and deleting it. 2 was skipped.
This can be avoided by using a copy of the list using the [:] operator:
for x in li[:]:
li.remove(x)
This finaly results in an empty list
Related
d = 3
cl = [1,3,4]
for i in cl:
if i <= d:
cl.remove(i)
print(cl)
output >> [3,4]
The number 3 should not be in the list as it passed the i <= d condition so cl.remove should've been called but it is in the output. What am I doing wrong here?
It happens because you're removing elements from list while iterating over it. It could be solved like this
[num for num in cl if num > d]
This works
d = 3
cl = [1, 3, 4]
for i in range(len(cl)):
if cl[i-1] <= d:
cl.remove(cl[i-1])
print(cl)
Please can you accept if it works...
This is the result of mutating a data structure during iteration. The for loop essentially creates an iterator over your list, with each item calling next(iterator). However, popping items off changes what the iterator is looking at
a = [1, 2, 3, 4]
it = iter(a)
# First iteration
next(it)
1
# remove element
a.pop(0)
1
# we skipped 2!
next(it)
3
Why? Well, we effectively changed what element the iterator is pointing to by removing the element we were currently on. We were looking at the first element in the sequence, but that was removed, so now the second element is the first one. So the call to next then points to the following element. This winds up looking like it was skipped when it wasn't, you just unintentionally had elements shuffled forward.
To avoid this, it's best to create a new list by filtering, as #AlexanderLekontsev suggests in his answer. This avoids mutating while iterating. You can do this with a standard loop with append like so:
newlist = []
for num in cl:
if num >= d:
newlist.append(num)
Essentially, because you are removing elements from the list while iterating over it, you have skipped over the value 3.
In the first iteration of your for-loop, you remove the value 1. Because of how iterations work in Python, in your second iteration of the for-loop, you are looking for next(next(cl)). But, cl has been updated to [3,4], since you removed 1. So now next(next(cl)) = next(3) = 4 and so you've skipped over 3.
You can resolve this by creating a new list and updating it as you go along. An easy way to do this using list comprehension is simply [num for num in cl if num > d].
If i have large list which runs in millions of items, i want to iterate through each of them. Once i use the item it will never be used again, so how do i delete the item from the list once used? What is the best approach?
I know numpy is fast and efficient but want to know how it can be done using normal list.
mylst = [item1, item2,............millions of items]
for each_item in mylist:
#use the item
#delete the item to free that memory
You cannot delete an object directly in Python - an object's memory is automatically reclaimed, by garbage collection, when it's no longer possible to reference the object. So long as an object is in a list, it may be referenced again later (via the list).
So you need to destroy the list too. For example, like so:
while mylst:
each_item = mylst.pop() # removes an object from the end of the list
# use the item
Assuming you can copy a list (memory constraints might cause issues here) and only need to remove specific elements from it, you can create a shallow copy of the list and remove elements from it while iterating through the original list:
a_list = [1, 2, 3, 4, 5]
b_list = a_list.copy()
removal_key = 0
for element in a_list:
if element % 2 == 0:
b_list.pop(removal_key)
removal_key -= 1; # we need to push the removal key back afer every deletion as our array b_list becomes smaller than the original after every deletion
removal_key += 1
print(b_list) #[1, 3, 5]
If creating the 2nd list is not an option, you can store the key's of elements to be removed from the list and then use a second list to remove them :
a_list = [1, 2, 3, 4, 5]
elements_to_remove = []
for key, element in enumerate(a_list):
if element % 2 == 0:
elements_to_remove.append(key)
removed_emelent_count = 0
for element in elements_to_remove:
a_list.pop(element - removed_emelent_count)
removed_emelent_count += 1
print(a_list) #[1, 3, 5]
Note that the 1st solution is more time efficient (especially when removing a lot of elements) while the 2nd solution is more memory efficient, especially when removing smal number of elements from the list.
This is probably the case in which you should use generators.
A generator is a function that returns an object which we can iterate over, one value at a time, using the special keyword yield instead of return.
They allows you to have a smaller memory footprint, by keeping only one element per iteration.
In python3.x, range is actually a generator (python2.x is xrange).
Overly simple example:
>>> def range(start, end):
... current = start
... while current < end:
... yield current
... current += 1
...
>>> for i in range(0, 2):
... print(i)
...
0
1
How is this million entries list made?
for x in check:
this = sorted(x) #the first tuple
for y in check:
that = sorted(y) #the other tuples in the list? in order to compare with 'this'.
if this == that:
check.remove(x)
print(check)
I basically want to check for every list (in the list 'check') if there are tuples that are the same, such as (1, 3) and (3, 1). Then I want to remove the the last one ((3,1)) out of the list 'check'. However, the function returns a "list.remove(x): x not in list" error when I use "check.remove(x)". When I used "check.remove(y)", the result was :
output of "check.remove(y)"
I noticed that the first tuple (of the tuple with the same value) got deleted and that in the second last list, that there is still a pair of tuples that have the same values.
How the list 'check' looks like
How can I compare the tuples with each other in the same list and remove the second one that contains the same values?
Repeated removal from a list is never a good a idea since it is O(N).
You can do the cleaning in one non-nested run-through, however. It is better to build a clean list from scratch and possibly reassign it to the same variable:
seen, no_dupes = set(), []
for c in check:
s = tuple(sorted(c))
if s not in seen:
seen.add(s)
no_dupes.append(c)
# check[:] = no_dupes # if you must
Use in and not ==
for x in check:
this = sorted(x) #the first tuple
for y in check:
that = sorted(y) #the other tuples in the list? in order to compare with 'this'.
if this in that:
check.remove(x)
# alternatively you might need to loop through this if its a tuple of tuples
# for t in this:
# if t in that:
# check.remove(x)
print(check)
Consider the instance [(1,1), (1,1), (1,1)]
In the first iteration, x is assigned to the first element in the list, y is also assigned to the first element, since x=y, remove x. Now when y is iterated to the second element, x=y, but now x has already been removed in the previous iteration. You should use dynamic programming:
new_check = []
for x in check:
this = sorted(x)
if x not in new_check:
new_check.append(x)
return new_check
So...
a = [2,3,4,5]
for x in a:
x += 1
a = [2,3,4,5]
Nada.
but if I ...
a[2] += 1
a = [2,3,5,5]
Clearly my mind fails to comprehend the basics. print(x) returns only the integer within the cell so it should simply add the one automatically for each list cell. What's the solution and what am I not grasping?
In this case you are defining a new variable x, that references each element of a in turn. You cannot modify the int that x refers to, because ints are immutable in Python. When you use the += operator, a new int is created and x refers to this new int, rather than the one in a. If you created a class that wrapped up an int, then you could use your loop as-is because instances of this class would be mutable. (This isn't necessary as Python provides better ways of doing what you want to do)
for x in a:
x += 1
What you want to do is generate a new list based on a, and possibly store it back to a.
a = [x + 1 for x in a]
To understand what's happening here, consider these two pieces of code. First:
for i in range(len(a)):
x = a[i]
x += 1
Second:
for x in a:
x += 1
These two for loops do exactly the same thing to x. You can see from the first that changing the value of x doesn't change a at all; the same holds in the second.
As others have noted, a list comprehension is a good way to create a new list with new values:
new_a = [x + 1 for x in a]
If you don't want to create a new list, you can use the following patterns to alter the original list:
for i in range(len(a)): # this gets the basic idea across
a[i] += 1
for i, _ in enumerate(a): # this one uses enumerate() instead of range()
a[i] += 1
for i, x in enumerate(a): # this one is nice for more complex operations
a[i] = x + 1
If you want to +1 on elements of a list of ints:
In [775]: a = [2,3,4,5]
In [776]: b=[i+1 for i in a]
...: print b
[3, 4, 5, 6]
Why for x in a: x += 1 fails ?
Because x is an immutable object that couldn't be modified in-place. If x is a mutable object, += might work:
In [785]: for x in a:
...: x+=[1,2,3] #here x==[] and "+=" does the same thing as list.extend
In [786]: a
Out[786]: [[1, 2, 3], [1, 2, 3]]
When you say
for x in a:
x += 1
Python simply binds the name x with the items from a on each iteration. So, in the first iteration x will be referring to the item which is in the 0th index of a. But when you say
x += 1
it is equivalent to
x = x + 1
So, you are adding 1 to the value of x and making x refer to the newly created number (result of x + 1). That is why the change is not visible in the actual list.
To fix this, you can add 1 to each and every element like this
for idx in range(len(a)):
a[idx] += 1
Now the same thing happens but we are replacing the old element at index i with the new element.
Output
[3, 4, 5, 6]
Note: But we have to prefer the list comprehension way whenever possible, since it leave the original list altered but constructs a new list based on the old list. So, the same thing can be done like this
a = [item + 1 for item in a]
# [3, 4, 5, 6]
The major difference is that, earlier we were making changes to the same list now we have created a new list and make a refer to the newly created list.
In your for loop, you declare a new variable x,
for x in a
It's this variable you next adds one to
x += 1
And then you do nothing with x.
You should save the xsomewhere if you want to use it later on :)
The variable x inside the for loop is a copy of each cell in the a list. If you modify x you will not affect a.
A more "correct" way to increment each element of a list by one is using a list comprehension:
a = [elem + 1 for elem in a]
You could also use the map function:
a = map(lambda x: x + 1, a)
when you put a[2], you are reffering to the third variable in the array 'a'
because the first element which in your case is 2 is stored at a[0] similarly, 3 at a[1] ,4 at a[2] and 5 at a[3]
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.