Asked  7 Months ago    Answers:  5   Viewed   31 times

I have a list

a = ["a", "b", "c", "d", "e"]

I want to remove elements in this list in a for loop like below:

for item in a:

But it doesn't work. What can I do?



You are not permitted to remove elements from the list while iterating over it using a for loop.

The best way to rewrite the code depends on what it is you're trying to do.

For example, your code is equivalent to:

for item in a:
a[:] = []

Alternatively, you could use a while loop:

while a:

I'm trying to remove items if they match a condition. Then I go to next item.

You could copy every element that doesn't match the condition into a second list:

result = []
for item in a:
    if condition is False:
a = result

Alternatively, you could use filter or a list comprehension and assign the result back to a:

a = filter(lambda item:... , a)


a = [item for item in a if ...]

where ... stands for the condition that you need to check.

Tuesday, June 1, 2021
answered 7 Months ago

When you write [x]*3 you get, essentially, the list [x, x, x]. That is, a list with 3 references to the same x. When you then modify this single x it is visible via all three references to it:

x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
    f"id(l[0]): {id(l[0])}n"
    f"id(l[1]): {id(l[1])}n"
    f"id(l[2]): {id(l[2])}"
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

To fix it, you need to make sure that you create a new list at each position. One way to do it is

[[1]*4 for _ in range(3)]

which will reevaluate [1]*4 each time instead of evaluating it once and making 3 references to 1 list.

You might wonder why * can't make independent objects the way the list comprehension does. That's because the multiplication operator * operates on objects, without seeing expressions. When you use * to multiply [[1] * 4] by 3, * only sees the 1-element list [[1] * 4] evaluates to, not the [[1] * 4 expression text. * has no idea how to make copies of that element, no idea how to reevaluate [[1] * 4], and no idea you even want copies, and in general, there might not even be a way to copy the element.

The only option * has is to make new references to the existing sublist instead of trying to make new sublists. Anything else would be inconsistent or require major redesigning of fundamental language design decisions.

In contrast, a list comprehension reevaluates the element expression on every iteration. [[1] * 4 for n in range(3)] reevaluates [1] * 4 every time for the same reason [x**2 for x in range(3)] reevaluates x**2 every time. Every evaluation of [1] * 4 generates a new list, so the list comprehension does what you wanted.

Incidentally, [1] * 4 also doesn't copy the elements of [1], but that doesn't matter, since integers are immutable. You can't do something like 1.value = 2 and turn a 1 into a 2.

Tuesday, June 1, 2021
answered 7 Months ago

"It would seem silly that Python would not have this kind of capability, because if not you wouldn't be able to redefine any variable passed through a for loop if it is part of a list that is being passed through." - That's how most programming languages work. To allow this capability would be bad because it would create something called side-effects, which make code obtuse.

Additionally this is a common programming pitfall because you should keep data out of variable names: see (especially the list of similar questions; even if you aren't dealing with variables names, you are at least trying to deal with the variable namespace). The remedy is to work at "one level higher": a list or set in this case. This is why your original question is not reasonable. (Some versions of python will let you hack the locals() dictionary, but this is unsupported and undocumented behavior and very poor style.)

You can however force python to use side-effects like so:

scores = [99.1, 78.3, etc.]
for i,score in enumerate(scores):
    scores[i] = int(score)

the above will round scores down in the scores array. The right way to do this however (unless you are working with hundreds of millions of elements) is to recreate the scores array like so:

scores = [...]
roundedScores = [int(score) for score in scores]

If you have many things you want to do to a score:

scores = [..., ..., ...]

def processScores(scores):
    '''Grades on a curve, where top score = 100%'''
    theTopScore = max(scores)

    def processScore(score, topScore):
        return 100-topScore+score

    newScores = [processScore(s,theTopScore) for s in scores]
    return newScores

sidenote: If you're doing float calculations, you should from __future__ import division or use python3, or cast to float(...) explicitly.

If you really want to modify what is passed in, you can pass in a mutable object. The numbers you are passing in are instances of immutable objects, but if for example you had:

class Score(object):
    def __init__(self, points):
        self.points = points
    def __repr__(self):
        return 'Score({})'.format(self.points)

scores = [Score(i) for i in [99.1, 78.3, ...]]
for s in scores:
    s.points += 5  # adds 5 points to each score

This would still be a non-functional way to do things, and thus prone to all the issues that side-effects cause.

Wednesday, August 4, 2021
answered 4 Months ago

Just use a containment test:

if redList in totalList:

This returns True for your sample data:

>>> totalList = [ [[0,1], [2,7], [6,3]], [[2,3], [6,1], [4,1]] ]
>>> redList = [ [0,1], [2,7], [6,3] ]
>>> redList in totalList
Thursday, August 5, 2021
answered 4 Months ago

The index is automatically incremented in the loop, you can skip an index with a where clause:

for index in 0..<5 where index != 2 {
Friday, August 13, 2021
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :