Python loop

Hello, i,ve got a problem with a loop in python,
i’m thinling it’s simple, but i don’t understand why python doesn’t make the last loop. this is my code:

#coding: utf-8
import rhinoscriptsyntax as rs
import os,webbrowser


export_file_name='E:\SCRIPT\Tmp export dxf'
objects=rs.GetObjects('selectionne l\'ensemble des contours à exporter par pièces.')

def CommandExport(save_folder,file_name,extension,objs):

    comm_string = '_-Export "{0}\\{1}{2}" _Enter'
    #print comm_string.format(save_folder,file_name,extension)
    rs.SelectObjects(objs)
    rs.Command(comm_string.format(save_folder,file_name,extension),False)
    rs.UnselectAllObjects()

for obj in objects:
    exp=[]
    exp.append(obj)
    objects.remove(obj)
    name=rs.ObjectName(obj)
    for item in objects:
        rs.SelectObject(item)
        if rs.ObjectName(item)==name:
            exp.append(item)
            objects.remove(item)
            rs.ObjectColor(item,(250,120,5))
           
    CommandExport(export_file_name,name,".dxf",exp)

i think it’s because i remove item of the list, but i don’t understand why…

thank you!!

excuse,me but i found another way simpliest to have what i want…

but to know i’ll be curious to understand how can we make a remove() methode on a list in a loop…
when we remove a item of a list, all index of next items will change… so the loop will not make that we want… that its?

You should never modify a list (or other collections) whose elements you are iterating through.

Instead you should create a list of elements you want to handle and need to remove from the objects list, then do what you need to do.

Changing lists and collections you are iterating over leads to undefined behavior.

Dear @nathanletwory
what about looping from the last index down to 0 ?
also bad practice ?
simple example - remove all names that contain “a” or “A”:

names = ["Tom","Bea","Michi","Michael","Otto","Max","Michaela","Uli"]
num = len(names)
searchString = "a"
for i in range(num-1,-1,-1):
  if searchString.lower() in names[i].lower():
    del names[i]
print("---shorter list---")
for n in names:
  print(n)

That uses an index variable and iterates over the range object (and calculates the upper bound as a one off length in advance too, which can avoid problems with while loop approaches). You can indeed mutate lists and dictionaries etc., however you like, forwards or backwards, this way. for i in range... is Python’s equivalent to C style loops.

The problem Nathan is referring to, occurs when you attempt to mutate a variable, when directly iterating over that same variable with a Pythonic loop that refers back to that variable each iteration, most commonly with dictionaries, e.g.

>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> for k, v in d.items():
...     if v % 2:
...         del d[k]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
1 Like

The reason the last loop is not being executed is because you are modifying the objects list while iterating over it. Specifically, you are removing elements from the list inside the outer for loop by calling objects.remove(obj) and objects.remove(item). This causes the loop to skip some elements and exit early.

A common way to avoid this issue is to iterate over a copy of the list, rather than the original list. You can do this by calling the list() function on the objects list before the outer loop, like this:

for obj in list(objects):
    ...

This creates a new list that contains the same elements as the original list, but is not linked to it. So when you remove elements from the original list, it does not affect the new list, and the loop continues to run until all elements have been processed.

Additionally, the inner for loop should also be using a copy of the object to iterate through otherwise it will skip some object.

for item in list(objects):

It should work fine

I was hoping to be able to take the first element of the list, then find in the list all the other objects that have the same name, once found, I delete them from the list so as not to go through them again and save time. , the latest version of code uses the rs.selectbyname() function, which greatly simplifies the problem. but if I did not have this function how should it proceed?

if i make an another list i will no save time…

You can order the elements by NAME, and then compare the NAME value in the current cycle to TEMP where TEMP=previous cycle name stored.
0)Order elements by → ascending, alphabetical etc
1)Store previous cycle value in a temporary variable
2)Compare current cycle value to the previous cycle value

So instead of having a clean list, you run all IDS, and before performing any operation you check if the current name = previous name

As long as you keep in mind that you cannot (should not) change lists you are iterating over at the same time.

1 Like