Python for loop skip all other loops?

I have a strange problem. Does anyone see something wrong with my code?

for x in questions: forms.append((SectionForm(request.POST, prefix=str(x.id)),x)) print "Appended " + str(x) for (form, question) in forms: print "Testing " + str(question) if form.is_valid(): forms.remove((form,question)) print "Deleted " + str(question) a = form.save(commit=False) a.audit = audit a.save() else: flag_error = True 

Results in:

 Appended Question 50 Appended Question 51 Appended Question 52 Testing Question 50 Deleted Question 50 Testing Question 52 Deleted Question 52 

It seems to have skipped question 51. It is added to the list, but the for loop skips it. Any ideas?

+4
source share
3 answers

You modify the contents of the forms object, which you repeat when you say:

 forms.remove((form,question)) 

According to the Python documentation for instructions, this is not safe (emphasis mine):

The for statement in Python is slightly different from what you can use in C or Pascal. Instead of always repeating the arithmetic progression of numbers (for example, in Pascal) or giving the user the ability to determine both the iteration step and the stop condition (like C), the Pythons operator to iterate over elements of any sequence (list or line), in the order they are occurrences in sequence.

It is not possible to change a sequence repeated in a loop (this can only happen for mutable types of sequences, such as lists). If you need to change the list that you are repeating (for example, to duplicate selected items), you must iterate over the copy. Slice notation makes this especially convenient:

 for x in a[:]: # make a slice copy of the entire list ... if len(x) > 6: a.insert(0, x) 

See also this paragraph from the Python Reference , which explains exactly what is happening:

There is subtlety when a sequence is modified by a cycle (this can happen only for mutable sequences, i.e. lists). An internal counter is used to track which item is used next, and this increases at each iteration. When this counter reaches the length of the sequence, the loop ends. This means that if a package removes the current (or previous) item from the sequence, the next item will be skipped (since it gets the index of the current item that has already been processed). Similarly, if a package inserts an element in a sequence before the current element, the current element will be processed again the next time through the loop.

There are many solutions. You can follow their advice and create a copy. Another possibility is to create a new list as a result of the second for loop instead of directly changing forms . The choice is up to you ...

+11
source

You remove objects from forms while iterating over it. This should result in the behavior you see ( http://docs.python.org/reference/compound_stmts.html#the-for-statement ).

The solution is to either iterate over a copy of this list, or add forms for deletion to a separate collection, and then perform the deletion subsequently.

+4
source

Using the remove method in forms (which I assume is a list) resizes the list. So think about it that way

 [ 50, 51, 52 ] 

Is your original list and you are requesting the first item. Then you remove this item from the list, so it looks like

 [51, 52] 

But now you are requesting a second item, so you get 52.

+2
source

Source: https://habr.com/ru/post/1401832/


All Articles