Tried deleting items in a list, no success.
>>> r = [1,2,3,4,5]
>>> for i in r:
if i<3:
del i
>>> print r
[1, 2, 3, 4, 5]
I even tried filtering it,
>>> def f(i):
True if i>2 else False
>>> print list(filter(f,r))
[]
I do not understand why the first one is not working. And I dont understand the result at all, when I use filter(function,iterable).
EDIT:
Seeing Paulo's comment below, now I do not understand why this works.
>>> for i in r:
if i<3:
r.remove(i)
>>> print r
[3, 4, 5]
Shouldn't the iterator problem be still there, and shouldn't the code end up removing only the first element (r[0])
Use a list comprehension instead:
[i for i in r if i >= 3]
and retain instead of delete.
Your filter never returned the test; so you always return None instead and that's false in a boolean context. The following works just fine:
def f(i):
return i > 2
Your initial attempt failed because del i unbinds i, but the list remains unaffected. Only the local name i is cleared.
If you want to delete an item from a list, you need to delete the index:
del r[0]
deletes the first element from the list.
Even if you did manage to delete indices the loop would have held some suprises:
>>> for i, element in enumerate(r):
... if element < 3:
... del r[i]
...
>>> r
[2, 3, 4, 5]
This version fails because the list iterator used by the for loop doesn't know you deleted elements from the list; deleting the value at index 0 shifts up the rest of the list, but the loop iterator looks at item 1 regardless:
first iteration, r = [1, 2, 3, 4, 5], iterator index 0 -> element = 1
second iteration, r = [2, 3, 4, 5], iterator index 1 -> element = 3
I do not understand why the first one is not working.
It is not working because the statement del i undefines the variable i - that is, it deletes it from the scope (global or local) which contains it.
And I dont understand the result at all, when I use filter(function,iterable)
Your function, f does not contain a return statement. Accordingly, it always returns None, which has the boolean equivalent value of False. Thus, filter excludes all values.
What you should probably be doing is filtering using a comprehension, and replacing the list, like so:
r = [i for i in r if i >= 3]
Or, if you really do want to delete part of the original list and modify it, use del on a slice of the list:
del r[:3]
Seeing Paulo's comment below, now I do not understand why [using remove] works.
Because remove(r) searches for the value r in the list, and deletes the first instance of it. Accordingly, repeated modification of the list does not affect the iteration that happens inside remove. However, note that it is still susceptible to the same error, if removal of an item leads to an item being skipped in iteration of the list.
Related
I am aware that if I wish to delete only the first element of a list t in Python I can do it with :
del t[0]
which is about as straightforward as it gets. However :
t = t[1:]
also works. In the textbook that I am learning from it says it is generally considered bad practice to use the latter approach since it does not delete the head of the list per se but
"The slice operator creates a new list and the assignment makes t refer to it, but none of that
has any effect on the list that was passed as an argument."
Why is this bad ? Can you name an example where such an approach would significantly alter a function ? Thanks in advance.
There are multiple reasons this is not a good idea:
Creating a new list just makes unnecessary work making the new list and deallocating the old list. And in between the two steps, twice the memory is used (because the original list and new list are alive at the same time, just prior to the assignment).
If something else refers to the same list, it does not get updated: u = t; del t[0] changes both u and t. But u = t; t = t[1:] assigns the new list to t while leaving u unchanged.
Lastly, del t[0] is clearer about its intension to remove the element than the more opaque t = t[1:].
Consider a function with the two implementations:
def remove_first_a(t):
t = t[1:]
def remove_first_b(t):
del t[0]
Now, see those functions in use:
> l = [1, 2, 3]
> remove_first_a(l)
> l
[1, 2, 3]
> remove_first_b(l)
> l
[2, 3]
The first implementation only reassigns the local variable t which has no effect on the object that was passed as a parameter. The second implementation actually mutates that object. The first function is rather useless in its present shape. You could change it:
def remove_first_a(t):
return t[1:]
> l = [1, 2, 3]
> x = remove_first_b(l)
> x
[2, 3]
Whether you want one or the other, depends more on the actual use case. Sometimes you want the original list to still be around unchanged for later use, and sometimes you want to make sure the original gets changed in all places that still have a reference to it.
just a example for the del and slice.
In [28]: u = t = [1, 2,3,4]
In [30]: id(u) == id(t) # now the id is same,they point one obj
Out[30]: True
if we use the del operator.
In [31]: del t[0]
In [32]: t
Out[32]: [2, 3, 4]
In [33]: u
Out[33]: [2, 3, 4]
but if we use the slice operator.
In [35]: t = t[1:]
In [36]: t
Out[36]: [2, 3, 4]
In [37]: id(t) == id(u)
Out[37]: False
In [39]: u
Out[39]: [1, 2, 3, 4]
and we found that t and u point different obj now.so we deal the list t, the list u is not change.
This code is only printing 1 2 4 5..My question is that why p is not updated with the new array at 3rd iteration
p = [1, 2, [1, 2, 3], 4, 5]
for each in p:
if type(each) == int:
print each
else:
p = each
Actually to be precise when debugged the code I saw that it actually updating the value of p but each variable is not reinitialised again.
Because of if type(each) == int: line. Your third element is a list ([1, 2, 3]) and not an int, so it doesn't print anything.
Now what comes to changing the p variable: p is just a name for an object, not the object itself. If you do p = each inside the for loop, it doesn't affect the original object you're looping through, it just changes the name p to a local name, which points to a different object. As soon as that round of the loop ends, your for loop continues to do its business with the original object you were looping through.
So, notice that p = each doesn't change the existing object (the p you're looping through), it simply creates a new local name p which points to the value of each.
What you most likely want is something like this:
p = [1, 2, [1, 2, 3], 4, 5]
for each in p:
if isinstance(each, list):
for x in each:
print x
else:
print each
This then again, this isn't recursive, and you'd need a function for that:
def print_elements(iterable):
for element in iterable:
if isinstance(element, list):
print_elements(element)
else:
print element
If you want to unpack the values into one list to use them for something other than printing, you should use something like this:
def recursive_unpack(iterable):
for element in iterable:
if isinstance(element, list):
yield from recursive_unpack(element)
else:
yield element
Why I'm using isinstance() instead of type(): Differences between isinstance() and type() in python
Also, if you want this to apply to all iterables (my last example) and not just lists: In Python, how do I determine if an object is iterable?
the issue is in the else statement, you're basically saying that if the each is not an int (in your case it's a list) then set the list p to the inside list.
I think that what you're trying to do can be accomplished by something like
p = [1, 2, [1, 2, 3], 4, 5]
for element in p:
if type(element) == int:
print element
else:
for otherElement in element:
if type(otherElement) == int:
print otherElement
the else statement in this case goes through the inside list and checks the elements it contains (otherElement)
As I am new to programming in Python. I am trying to remove particular elements from array using for loop which looks like
a=[2,3,1,4,1,1,1,5]
n=a.count(1)
for i in range (len(a)-n):
if (a[i]==1):
del a[i]
else:
a[i]=a[i]
print (a)
I want to remove 1 from array a. But, I am getting result as:
[2, 3, 4, 1, 1, 5].
That is 1 still exists in my new array. Can somebody please answer my problem?
try like this:
a = [2,3,1,4,1,1,1,5]
a = [x for x in a if x!=1] # this is called list comprehension
note Never modify list while iterating
Use a while loop and the remove method:
a = [2, 3, 1, 4, 1, 1, 1, 5]
while 1 in a:
a.remove(1)
print a
The real answer to your question (which none of the other answers addresses) is that every time you remove an item, the index i moves past it.
in your case:
a = [2,3,1,4,1,1,1,5]
after deleting the 5th item in the original list, the pointer moves to the 6th item, and the new 5th item (the second 1 in the sequence of three 1s) is skipped.
Regarding the comment never modify a list in a loop, try to implement an in-place algorithm like Fisher-Yates without modifying the list. Never say never. Know what you're doing.
The OP changes the list in-place, not creating a new list.
There are two methods, the second is safe, the first might be faster.
a = [2, 3, 1, 4, 1, 1, 1, 5]
toremove = 1
for i in range(len(a)-1, -1, -1):
if a[i] == toremove:
del a[i]
and
a = [2, 3, 1, 4, 1, 1, 1, 5]
toremove = 1
for i in range(a.count(toremove)):
a.remove(toremove)
The second removes the element however many times it exists (before the loop). Since we are not iterating on the list, it is safe to use the remove method.
Both fragments should be O(n) (but haven't done the calculations).
You can copy a and then remove but you cannot iterate over and delete elements from the same list, if your list starts with n elements python will have n pointers to each element so removing elements from the list as your are iterating over it will cause elements to be missed.python has no way of knowing you have removed elements from the list:
a = [2,3,1,4,1,1,1,5]
for ele in a[:]:
if ele == 1:
a.remove(1)
print(a)
[2, 3, 4, 5]
You can also use reversed which returns and iterator avoiding creating a whole copy of the list at once:
a = [2,3,1,4,1,1,1,5]
for ele in reversed(a):
if ele == 1:
a.remove(1)
print(a)
[2, 3, 4, 5]
Or using a list comprehension with the [:] syntax so we actually update the original object:
a[:] = (ele for ele in a if ele != 1)
All the above are linear operations using a single pass over a.
Actually as the del statement will remove elements from your list , and as the list that you bound in your loop doesn't been update after the first deleting you remove incorrect elements from your list , so if you want to use del you need to make the list name in your loop to reference to new list , that you can use a function for this aim , but as a more python way you can just use a list comprehension:
>>> a=[2,3,1,4,1,1,1,5]
>>> a=[i for i in a if i !=1]
>>> a
[2, 3, 4, 5]
Or you can use filter :
>>> a=[2,3,1,4,1,1,1,5]
>>> a=filter(lambda x: x !=1,a)
>>> a
[2, 3, 4, 5]
I'd make a function in python, that given a list returns a list of list, in which every element is the list given decreased by one.
Input: list_decreaser([0,3,4,5,6,7,8)
Output: [[0,3,4,5,6,7],[0,3,4,5,6],[0,3,4,5],[0,3,4],[0,3],[0]]
My attempt:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list)
return listresult
The code appends the same list multiple times. It should append copy of the list.
And use del list[..] instead of list.remove(list[..]) to delete an item at specific index.
def list_decreaser(xs):
listresult = []
for i in range(len(xs)-1, 0, -1): # <--- interate backward
del xs[i]
listresult.append(xs[:]) # <----
return listresult
print(list_decreaser([0,3,4,5,6,7,8]))
Or using list comprehension:
>>> xs = [0,3,4,5,6,7,8]
>>> [xs[:i] for i in range(len(xs)-1, 0, -1)]
[[0, 3, 4, 5, 6, 7], [0, 3, 4, 5, 6], [0, 3, 4, 5], [0, 3, 4], [0, 3], [0]]
BTW, don't use list as a variable name. It shadows builtin list function.
The problem is that you're appending the same list over and over again. You keep mutating the list in-place, but you're never creating a new list. So you end up with a list of N references to the same empty list.
This is the same problem discussed in two FAQ questions. I think How do I create a multidimensional list explains it best.
Anyway, what you need to do is append a new list each time through the loop. There are two ways to do that.
First, you can append a copy of the current list, instead of the list itself:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list[:]) # this is the only change
return listresult
This solves your problem, but it leaves a few new problems:
First, list.remove(list[x]) is a very bad idea. If you give it, say, [0, 1, 2, 0], what happens when you try to remove that second 0? You're calling list.remove(0), and there's no way the list can know you wanted the second 0 rather than the first! The right thing to do is call del list[x] or list.pop(x).
But once you fix that, you're removing the elements from the wrong side. x is 0, then 1, then 2, and so on. You remove element 0, then element 1 (which is the original element 2), then element 2 (which is the original element 4), and eventually get an IndexError. Even if you fixed the "skipping an index" issue (which is also explained in the FAQ somewhere), you'd still be removing the first elements rather than the last ones. You can fix that by turning the range around. However, there's an even easier way: Just remove the last element each time, instead of trying to figure out which x is the right thing, which you can do by specifying -1, or just calling pop with no argument. And then you can use a much simpler loop, too:
def list_decreaser(list):
listresult = []
while list:
list.pop()
listresult.append(list[:])
return listresult
Of course this appends the last, empty list, which you apparently didn't want. You can fix that by doing while len(list) >= 1, or putting an if list: listresult.append(list[:]), or in various other ways.
Alternatively, you can make new truncated lists instead of truncating and copying the same list over and over:
def list_decreaser(list):
listresult = []
while len(list):
list = list[:-1]
listresult.append(list)
return listresult
Note that in this second version, rather than changing the value stored in list, we're creating a new list and storing that new list in list.
use this
def list_decreaser(list1):
listresult = []
for i in list1:
list1 = list[:-1]
listresult.append(list1)
return listresult
When I loop over a list, the name that I give within the loop to the elements of the list apparently refers directly to each element in turn, as evidenced by:
>>> a = [1, 2, 3]
>>> for n in a:
... print n is a[a.index(n)]
True
True
True
So why doesn't this seem to do anything?
>>> for n in a: del n
>>> a
[1, 2, 3]
If I try del a[a.index(n)], I get wonky behavior, but at least it's behavior I can understand - every time I delete an element, I shorten the list, changing the indices of the other elements, so I end up deleting every other element of the list:
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for n in a: del a[a.index(n)]
>>> a
[1, 3, 5, 7, 9]
Clearly I'm allowed to delete from the list while iterating. So what's going on when I try to del n inside the loop? Is anything being deleted?
Inside the block of any for n in X statement, n refers to the variable named n itself, not to any notion of "the place in the list of the last value you iterated over". Therefore, what your loop is doing is repeatedly binding a variable n to a value fetched from the list and then immediately unbinding that same variable again. The del n statement only affects your local variable bindings, rather than the list.
Because you're doing this:
some_reference = a[0]
del some_reference
#do you expect this to delete a[0]? it doesn't.
You're operating on a variable bound to the value of a[0] (then one bound to a[1], then...). You can delete it, but it won't do anything to a.
Others have explained the idea of deleting a reference pretty well, I just wanted to touch on item deletion for completeness. Even though you use similar syntax, del a[1], each type will handle item deletion a little differently. As expected, deleting items from most containers just removes them, and some types do not support item deleting at all, tuples for example. Just as a fun exerciser:
class A(object):
def __delitem__(self, index):
print 'I will NOT delete item {}!'.format(index)
a = A()
del a[3]
# I will NOT delete item 3!
Dolda2000's answer covers the main question very nicely, but there are three other issues here.
Using index(n) within a for n in a loop is almost always a bad idea.
It's incorrect:
a = [1, 2, 1, 2]
for n in a:
print(a.index(n))
This will print 0, then 1, then 0 again. Why? Well, the third value is 1. a.index(1) is the index of the first 1 in the list. And that's 0, not 2.
It's also slow: To find the ith value, you have to check the first i elements in the list. This turns a simple linear (fast) algorithm into a quadratic (slow) one.
Fortunately, Python has a nice tool to do exactly what you want, enumerate:
for i, n in enumerate(a):
print(i)
Or, if you don't need the values at all, just the indices:
for i in len(range(a)):
print(i)
(This appears as a hint in the docs at least twice, conveniently buried in places no novice would ever look. But it's also spelled out early in the tutorial.)
Next, it looks like you were attempting to test for exactly the case Dolda2000 explained was happening, with this:
n is a[a.index(n)]
Why didn't that work? You proved that they are the same object, so why didn't deleting it do anything?
Unlike C-family languages, where variables are addresses where values (including references to other addresses) get stored, Python variables are names that you bind to values that exist on their own somewhere else. So variables can't reference other variables, but they can be names for the same value. The is expression tests whether two expressions name the same value. So, you proved that you had two names for the same value, you deleted one of those names, but the other name, and the value, are still there.
An example is worth 1000 words, so run this:
a = object() # this guarantees us a completely unique value
b = a
print a, b, id(a), id(b), a is b
del b
print a, id(a)
(Of course if you also del a, then at some point Python will delete the value, but you can't see that, because by definition you no longer have any names to look at it with.)
Clearly I'm allowed to delete from the list while iterating.
Well, sort of. Python leaves it undefined what happens when you mutate an iterable while iterating over it—but it does have special language that describes what happens for builtin mutable sequences (which means list) in the for docs), and for dicts (I can't remember where, but it says somewhere that it can't guarantee to raise a RuntimeError, which implies that it should raise a RuntimeError).
So, if you know that a is a list, rather than some subclass of list or third-party sequence class, you are allowed to delete from it while iterating, and to expect the "skipping" behavior if the elements you're deleting are at or to the left of the iterator. But it's pretty hard to think of a realistic good use for that knowledge.
This is what happens when you do
for n in a: del a[a.index(n)]
Try this:
a = [0, 1, 2, 3, 4]
for n in a: print(n, a); del a[a.index(n)]
This is what you get:
(0, [0, 1, 2, 3, 4])
(2, [1, 2, 3, 4])
(4, [1, 3, 4])
Thus n is just a tracker of the index, and you can think this way. Every time the function iterates, n moves on to the next relative position in the iterable.
In this case, n refers to a[0] for the first time, a[1] for the second time, a[2] for the third time. After that there is no nextItem in the list, thus the iterations stops.