Python-интерпретатор завершает цикл до завершения итерации: - PullRequest
0 голосов
/ 28 февраля 2019

Я создаю скрипт Python для выбора и открытия файлов из произвольного набора подкаталогов.Для этого я создаю функцию с циклом for, которая составляет список всех файлов в каталоге, и еще один список всех подкаталогов в этом каталоге.Затем другой цикл for изменяется для каждого из подкаталогов путем рекурсивного вызова той же функции.Я неоднократно проходил через эту функцию в режиме отладки и очень внимательно следил за каждой переменной, поскольку она меняется шаг за шагом.

Я совершенно сбит с толку, потому что каждый цикл работает немного, но затем завершается без завершения.Обычно в программировании вещи работают полностью или не работают вообще, но очень редко они функционируют только наполовину.Оба цикла делают это, и когда второй вызов функции завершается, он не возвращается к первому вызову для завершения второго цикла, а полностью завершает все вызовы функции.Вот код, я был бы очень признателен за вашу помощь:

def descend(directory, depth, dive=-1): # dive=-1 before we have touched the surface 
    global choices                      # list of files to be chosen from later  
    os.chdir(directory)                 # change to target directory 
    dirlist = []                        # empty the list of directories within the directory   
    baseList = os.listdir(directory)    # list of all files and directories

    if dive < depth:                    # depth corresponds to the levels of sub-directories to be searched
        dive += 1                       # effectively starts at 0

        fileIndex=0                     # index as we step through the baseList
        for x in baseList:              # why doesn't this loop complete?
            if os.path.isfile(x) == True:   # if it's a regular file
                path = directory + '/' + baseList.pop(fileIndex)    # create its full path
                choices.append(path)                                # and add that to our list of choices
            elif os.path.isdir(x) == True:                  # if it's a directory
                dirlist.append(baseList.pop(fileIndex))     # add to list of directories to recursively search
            fileIndex += 1      # add 1 to the file index

        for x in dirlist:       # for each item in the list of directories
            nextdir = directory + '/' + dirlist.pop()   # make a variable of its full path
            descend(nextdir, depth, dive)               # pass that back to this function and repeat

Я даже пробовал глупые решения, такие как добавление if len(baseList) != 0: continue к нижней части циклов, которые не нужно повторять дважды, но это делаетне поможет ни один из них.Я в полном замешательстве.

1 Ответ

0 голосов
/ 01 марта 2019

Хорошо, я собираюсь ответить на свой вопрос здесь, потому что мне было любопытно и я провел еще какое-то исследование, и это слишком долго для комментария.Может быть, это поможет прояснить это для кого-то еще.После выяснения того, что происходило во время игры с IDLE, я обнаружил это в Pydoc:

Примечание: есть тонкость, когда последовательность модифицируется циклом (это может происходить только для изменяемых последовательностей, например,списки).Внутренний счетчик используется для отслеживания того, какой элемент используется следующим, и он увеличивается на каждой итерации.Когда этот счетчик достигнет длины последовательности, цикл завершается.Это означает, что если набор удаляет текущий (или предыдущий) элемент из последовательности, следующий элемент будет пропущен (поскольку он получает индекс текущего элемента, который уже был обработан).Аналогично, если набор вставляет элемент в последовательность перед текущим элементом, текущий элемент будет обработан снова в следующий раз в цикле.Это может привести к неприятным ошибкам, которых можно избежать, сделав временную копию с использованием фрагмента всей последовательности, например,

 for x in a[:]:
     if x < 0: a.remove(x)

Ввод и вывод из моего эксперимента IDLE:

>>> baselist=[0,1,2,3,4,5,6,7,8,9]
>>> listeven=[]
>>> listodd=[]
>>> index=0
>>> for x in baselist:
        print('baselist is ', baselist)
        print('listeven is ', listeven)
        print('listodd is ', listodd)
        print('x is ', x)
        print('index is ', index)
        if x % 2 == 0:
            print('appending ', x, ' to listeven by popping from baselist')
            listeven.append(baselist.pop(index))
        else:
            print('appending ', x, ' to listodd by popping from baselist')
            listodd.append(baselist.pop(index))
        index += 1

baselist is  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
listeven is  []
listodd is  []
x is  0
index is  0
appending  0  to listeven by popping from baselist
baselist is  [1, 2, 3, 4, 5, 6, 7, 8, 9]
listeven is  [0]
listodd is  []
x is  2
index is  1
appending  2  to listeven by popping from baselist
baselist is  [1, 3, 4, 5, 6, 7, 8, 9]
listeven is  [0, 2]
listodd is  []
x is  4
index is  2
appending  4  to listeven by popping from baselist
baselist is  [1, 3, 5, 6, 7, 8, 9]
listeven is  [0, 2, 4]
listodd is  []
x is  6
index is  3
appending  6  to listeven by popping from baselist
baselist is  [1, 3, 5, 7, 8, 9]
listeven is  [0, 2, 4, 6]
listodd is  []
x is  8
index is  4
appending  8  to listeven by popping from baselist
>>> baselist
[1, 3, 5, 7, 9]

Удаление элементов из списка в середине цикла заставляет интерпретатора думать, что он завершил итерацию раньше, потому что его скрытый внутренний индекс сообщает, что следующий индекс находится вне диапазона, что означает, что некоторые значения пропущены!

Теперь этофункция выглядит так:

def descend(directory, depth, dive=-1): 
global files
os.chdir(directory)
subBranches = []
baseList = os.listdir(directory)
if dive < depth:
    dive += 1
    for x in baseList:
        if os.path.isfile(x) == True:
            path = directory + '/' + x
            files.append(path)
        elif os.path.isdir(x) == True:
            subBranches.append(x)
    for x in subBranches:
        nextdir = directory + '/' + x
        descend(nextdir, depth, dive)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...