izip_longest в itertools: что здесь происходит? - PullRequest
7 голосов
/ 14 марта 2011

Я изо всех сил пытаюсь понять, как работает приведенный ниже код.Это от http://docs.python.org/library/itertools.html#itertools.izip_longest, и является чистым эквивалентом Python итератора izip_longest.Меня особенно озадачивает функция часового, как она работает?

def izip_longest(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
        yield counter()         # yields the fillvalue, or raises IndexError
    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except IndexError:
        pass

Ответы [ 3 ]

6 голосов
/ 14 марта 2011

Хорошо, мы можем сделать это.О страже.Выражение ([fillvalue]*(len(args)-1)) создает список, содержащий одно значение заполнения для каждой итерации в args, минус один.Итак, для примера выше ['-'].Затем counter присваивается функция pop этого списка.sentinel сам по себе является генератором , который выводит один элемент из этого списка на каждой итерации.Вы можете перебирать каждый итератор, возвращаемый sentinel ровно один раз, и он всегда будет давать fillvalue.Общее количество предметов, получаемых всеми итераторами, возвращаемыми sentinel, равно len(args) - 1 (спасибо Свену Марнаху за разъяснение этого, я неправильно понял).

Теперь проверьте это:

iters = [chain(it, sentinel(), fillers) for it in args]

Это хитрость.iters - это список, который содержит итератор для каждой итерации в args.Каждый из этих итераторов выполняет следующие действия:

  1. Итерирует по всем элементам в соответствующей итерации от args.
  2. Итерирует по часовой стрелке один раз, получая fillvalue.
  3. Повторите fillvalue для всей вечности.

Теперь, как установлено ранее, мы можем только перебрать всех стражей вместе len(args)-1 раз, прежде чем он выдаст IndexError.Это хорошо, потому что один из итераций самый длинный.Итак, когда мы дошли до того, что IndexError поднято, это означает, что мы завершили итерацию по самой длинной итерации в args.

Добро пожаловать.

PS: надеюсь, это понятно.

5 голосов
/ 14 марта 2011

Функция sentinel() возвращает итераторы, выдающие fillvalue ровно один раз.Общее число fillvalue с, полученное всеми итераторами, возвращаемыми sentinel(), ограничено n-1, где n - количество итераторов, переданных izip_longest().После того, как это число fillvalue s будет исчерпано, дальнейшая итерация по итератору, возвращенному sentinel(), вызовет IndexError.

Эта функция используется для определения того, были ли исчерпаны все итераторы: Каждый итераторchain() ed с итератором, возвращаемым sentinel().Если все итераторы исчерпаны, итератор, возвращаемый sentinel(), будет повторяться в течение n-го раза, что приводит к IndexError, вызывая конец izip_longest() по очереди.

Пока чтоЯ объяснил, что делает sentinel(), а не как это работает.Когда вызывается izip_longest(), оценивается определение sentinel().При оценке определения также оценивается аргумент по умолчанию sentinel(), один раз за вызов izip_longest().Код эквивалентен

fillvalue_list = [fillvalue] * (len(args)-1)
def sentinel():
    yield fillvalue_list.pop()

Сохранение этого в аргументе по умолчанию, а не в переменной во вложенной области видимости - это просто оптимизация, равно как и включение .pop в аргумент по умолчанию, поскольку оно сохраняетпросматривая его каждый раз, когда итератор, возвращаемый sentinel(), повторяется.

2 голосов
/ 14 марта 2011

Определение sentinel почти эквивалентно

def sentinel():
    yield ([fillvalue] * (len(args) - 1)).pop()

за исключением того, что он получает связанный метод pop (объект функции) в качестве аргумента по умолчанию. Аргумент по умолчанию оценивается во время определения функции, поэтому один раз за вызов izip_longest вместо одного раза за вызов sentinel. Поэтому объект функции «запоминает» список [fillvalue] * (len(args) - 1) вместо того, чтобы создавать его заново при каждом вызове.

...