Автор оригинала: Doug Hellmann.
Цель:
В модуль itertools входит набор функций для работы с наборы данных последовательности.
Функции, предоставляемые itertools
, вдохновлены аналогичными функциями языков функционального программирования, таких как Clojure, Haskell, APL и SML. Они предназначены для быстрой работы и эффективного использования памяти, а также для объединения вместе для выражения более сложных алгоритмов, основанных на итерациях.
Код на основе итератора предлагает лучшие характеристики потребления памяти, чем код, использующий списки. Поскольку данные не производятся итератором до тех пор, пока они не понадобятся, нет необходимости хранить все данные в памяти одновременно. Эта «ленивая» модель обработки может уменьшить количество подкачки и другие побочные эффекты больших наборов данных, повышая производительность.
В дополнение к функциям, определенным в itertools
, примеры в этом разделе также полагаются на некоторые встроенные функции для итерации.
Слияние и разделение итераторов
Функция chain ()
принимает несколько итераторов в качестве аргументов и возвращает единственный итератор, который производит содержимое всех входных данных, как если бы они поступили от одного итератора.
itertools_chain.py
from itertools import * for i in chain([1, 2, 3], ['a', 'b', 'c']): print(i, end' ') print()
chain ()
позволяет легко обрабатывать несколько последовательностей без создания одного большого списка.
$ python3 itertools_chain.py 1 2 3 a b c
Если не все итерации, которые нужно объединить, заранее известны или их нужно оценивать лениво, для построения цепочки можно использовать chain.from_iterable ()
.
itertools_chain_from_iterable.py
from itertools import * def make_iterables_to_chain(): yield [1, 2, 3] yield ['a', 'b', 'c'] for i in chain.from_iterable(make_iterables_to_chain()): print(i, end' ') print()
$ python3 itertools_chain_from_iterable.py 1 2 3 a b c
Встроенная функция zip ()
возвращает итератор, который объединяет элементы нескольких итераторов в кортежи.
itertools_zip.py
for i in zip([1, 2, 3], ['a', 'b', 'c']): print(i)
Как и в случае с другими функциями в этом модуле, возвращаемое значение – это повторяемый объект, который генерирует значения по одному.
$ python3 itertools_zip.py (1, 'a') (2, 'b') (3, 'c')
zip ()
останавливается, когда исчерпывается первый итератор ввода. Чтобы обработать все входные данные, даже если итераторы выдают разное количество значений, используйте zip_longest ()
.
itertools_zip_longest.py
from itertools import * r1 range(3) r2 range(2) print('zip stops early:') print(list(zip(r1, r2))) r1 range(3) r2 range(2) print('\nzip_longest processes all of the values:') print(list(zip_longest(r1, r2)))
По умолчанию zip_longest ()
заменяет все отсутствующие значения None
. Используйте аргумент fillvalue
, чтобы использовать другое замещающее значение.
$ python3 itertools_zip_longest.py zip stops early: [(0, 0), (1, 1)] zip_longest processes all of the values: [(0, 0), (1, 1), (2, None)]
Функция islice ()
возвращает итератор, который возвращает выбранные элементы из итератора ввода по индексу.
itertools_islice.py
from itertools import * print('Stop at 5:') for i in islice(range(100), 5): print(i, end' ') print('\n') print('Start at 5, Stop at 10:') for i in islice(range(100), 5, 10): print(i, end' ') print('\n') print('By tens to 100:') for i in islice(range(100), 0, 100, 10): print(i, end' ') print('\n')
islice ()
принимает те же аргументы, что и оператор среза для списков: start
, stop
и step
. Аргументы start и step необязательны.
$ python3 itertools_islice.py Stop at 5: 0 1 2 3 4 Start at 5, Stop at 10: 5 6 7 8 9 By tens to 100: 0 10 20 30 40 50 60 70 80 90
Функция tee ()
возвращает несколько независимых итераторов (по умолчанию 2) на основе одного исходного ввода.
itertools_tee.py
from itertools import * r islice(count(), 5) i1, i2 tee(r) print('i1:', list(i1)) print('i2:', list(i2))
tee ()
имеет семантику, аналогичную служебной программе Unix tee
, которая повторяет значения, считываемые со своего ввода, и записывает их в именованный файл и стандартный вывод. Итераторы, возвращаемые функцией tee ()
, могут использоваться для передачи одного и того же набора данных в несколько алгоритмов для параллельной обработки.
$ python3 itertools_tee.py i1: [0, 1, 2, 3, 4] i2: [0, 1, 2, 3, 4]
Новые итераторы, созданные tee ()
, совместно используют свои входные данные, поэтому исходный итератор не следует использовать после создания новых.
itertools_tee_error.py
from itertools import * r islice(count(), 5) i1, i2 tee(r) print('r:', end' ') for i in r: print(i, end' ') if i > 1: break print() print('i1:', list(i1)) print('i2:', list(i2))
Если значения потребляются из исходного ввода, новые итераторы не будут создавать эти значения:
$ python3 itertools_tee_error.py r: 0 1 2 i1: [3, 4] i2: [3, 4]
Преобразование входов
Встроенная функция map ()
возвращает итератор, который вызывает функцию для значений во входных итераторах и возвращает результаты. Он останавливается, когда любой итератор ввода исчерпан.
itertools_map.py
def times_two(x): return 2 * x def multiply(x, y): return (x, y, x * y) print('Doubles:') for i in map(times_two, range(5)): print(i) print('\nMultiples:') r1 range(5) r2 range(5, 10) for i in map(multiply, r1, r2): print('{:d} * {:d} = {:d}'.format(*i)) print('\nStopping:') r1 range(5) r2 range(2) for i in map(multiply, r1, r2): print(i)
В первом примере лямбда-функция умножает входные значения на 2. Во втором примере лямбда-функция умножает два аргумента, взятых из отдельных итераторов, и возвращает кортеж с исходными аргументами и вычисленным значением. Третий пример останавливается после создания двух кортежей, потому что второй диапазон исчерпан.
$ python3 itertools_map.py Doubles: 0 2 4 6 8 Multiples: 0 * 5 = 0 1 * 6 = 6 2 * 7 = 14 3 * 8 = 24 4 * 9 = 36 Stopping: (0, 0, 0) (1, 1, 1)
Функция starmap ()
похожа на map ()
, но вместо создания кортежа из нескольких итераторов она разделяет элементы в одном итераторе в качестве аргументов сопоставления. функция с использованием синтаксиса *
.
itertools_starmap.py
from itertools import * values [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)] for i in starmap(lambda x, y: (x, y, x * y), values): print('{} * {} = {}'.format(*i))
Если функция сопоставления с map ()
называется f (i1, i2)
, функция сопоставления, переданная в starmap ()
, называется f (* i)
.
$ python3 itertools_starmap.py 0 * 5 = 0 1 * 6 = 6 2 * 7 = 14 3 * 8 = 24 4 * 9 = 36
Создание новых ценностей
Функция count ()
возвращает итератор, который производит последовательные целые числа на неопределенный срок. Первое число можно передать в качестве аргумента (по умолчанию – ноль). Нет аргумента верхней границы (см. Встроенный range ()
для большего контроля над набором результатов).
itertools_count.py
from itertools import * for i in zip(count(1), ['a', 'b', 'c']): print(i)
Этот пример останавливается, так как аргумент списка использован.
$ python3 itertools_count.py (1, 'a') (2, 'b') (3, 'c')
Аргументы start и step для count ()
могут быть любыми числовыми значениями, которые можно складывать вместе.
itertools_count_step.py
import fractions from itertools import * start fractions.Fraction(1, 3) step fractions.Fraction(1, 3) for i in zip(count(start, step), ['a', 'b', 'c']): print('{}: {}'.format(*i))
В этом примере начальная точка и шаги – это объекты Fraction
из модуля Fraction
.
$ python3 itertools_count_step.py 1/3: a 2/3: b 1: c
Функция cycle ()
возвращает итератор, который бесконечно повторяет содержимое переданных ей аргументов. Поскольку он должен запоминать все содержимое итератора ввода, он может потреблять довольно много памяти, если итератор длинный.
itertools_cycle.py
from itertools import * for i in zip(range(7), cycle(['a', 'b', 'c'])): print(i)
В этом примере переменная счетчика используется для выхода из цикла после нескольких циклов.
$ python3 itertools_cycle.py (0, 'a') (1, 'b') (2, 'c') (3, 'a') (4, 'b') (5, 'c') (6, 'a')
Функция repeat ()
возвращает итератор, который выдает одно и то же значение каждый раз, когда к нему обращаются.
itertools_repeat.py
from itertools import * for i in repeat('over-and-over', 5): print(i)
Итератор, возвращаемый функцией repeat ()
, продолжает возвращать данные вечно, если не указан необязательный аргумент times
, чтобы ограничить его.
$ python3 itertools_repeat.py over-and-over over-and-over over-and-over over-and-over over-and-over
Полезно сочетать repeat ()
с zip ()
или map ()
, когда инвариантные значения необходимо включить со значениями из других итераторы.
itertools_repeat_zip.py
from itertools import * for i, s in zip(count(), repeat('over-and-over', 5)): print(i, s)
В этом примере значение счетчика объединяется с константой, возвращаемой функцией repeat ()
.
$ python3 itertools_repeat_zip.py 0 over-and-over 1 over-and-over 2 over-and-over 3 over-and-over 4 over-and-over
В этом примере map ()
используется для умножения чисел в диапазоне от 0 до 4 на 2.
itertools_repeat_map.py
from itertools import * for i in map(lambda x, y: (x, y, x * y), repeat(2), range(5)): print('{:d} * {:d} = {:d}'.format(*i))
Итератор repeat ()
не нуждается в явном ограничении, поскольку map ()
прекращает обработку, когда заканчивается любой из его входов, а range ()
возвращает только пять элементов.
$ python3 itertools_repeat_map.py 2 * 0 = 0 2 * 1 = 2 2 * 2 = 4 2 * 3 = 6 2 * 4 = 8
Фильтрация
Функция drop while ()
возвращает итератор, который создает элементы итератора ввода после того, как условие впервые становится ложным.
itertools_dropwhile.py
from itertools import * def should_drop(x): print('Testing:', x) return x < 1 for i in dropwhile(should_drop, [-1, 0, 1, 2, -2]): print('Yielding:', i)
drop while ()
не фильтрует каждый элемент ввода; после того, как условие ложно в первый раз, возвращаются все оставшиеся элементы ввода.
$ python3 itertools_dropwhile.py Testing: -1 Testing: 0 Testing: 1 Yielding: 1 Yielding: 2 Yielding: -2
Противоположностью drop while ()
является takewhile ()
. Он возвращает итератор, который возвращает элементы из итератора ввода, пока тестовая функция возвращает true.
itertools_takewhile.py
from itertools import * def should_take(x): print('Testing:', x) return x < 2 for i in takewhile(should_take, [-1, 0, 1, 2, -2]): print('Yielding:', i)
Как только should_take ()
возвращает False
, takewhile ()
прекращает обработку ввода.
$ python3 itertools_takewhile.py Testing: -1 Yielding: -1 Testing: 0 Yielding: 0 Testing: 1 Yielding: 1 Testing: 2
Встроенная функция filter ()
возвращает итератор, который включает только элементы, для которых тестовая функция возвращает true.
itertools_filter.py
from itertools import * def check_item(x): print('Testing:', x) return x < 1 for i in filter(check_item, [-1, 0, 1, 2, -2]): print('Yielding:', i)
filter ()
отличается от drop while ()
и takewhile ()
тем, что каждый элемент проверяется перед возвратом.
$ python3 itertools_filter.py Testing: -1 Yielding: -1 Testing: 0 Yielding: 0 Testing: 1 Testing: 2 Testing: -2 Yielding: -2
filterfalse ()
возвращает итератор, который включает только те элементы, для которых тестовая функция возвращает false.
itertools_filterfalse.py
from itertools import * def check_item(x): print('Testing:', x) return x < 1 for i in filterfalse(check_item, [-1, 0, 1, 2, -2]): print('Yielding:', i)
Тестовое выражение в check_item ()
такое же, поэтому результаты в этом примере с filterfalse ()
противоположны результатам из предыдущего примера.
$ python3 itertools_filterfalse.py Testing: -1 Testing: 0 Testing: 1 Yielding: 1 Testing: 2 Yielding: 2 Testing: -2
compress ()
предлагает другой способ фильтрации содержимого итерируемого объекта. Вместо вызова функции он использует значения в другой итерации, чтобы указать, когда принимать значение, а когда игнорировать.
itertools_compress.py
from itertools import * every_third cycle([False, False, True]) data range(1, 10) for i in compress(data, every_third): print(i, end' ') print()
Первый аргумент – это данные, которые можно обрабатывать, и второй – итеративный селектор, производящий логические значения, указывающие, какие элементы следует брать из входных данных (истинное значение вызывает создание значения, ложное значение заставляет его игнорировать).
$ python3 itertools_compress.py 3 6 9
Группировка данных
Функция groupby ()
возвращает итератор, который создает наборы значений, упорядоченные по общему ключу. Этот пример иллюстрирует группировку связанных значений на основе атрибута.
itertools_groupby_seq.py
import functools from itertools import * import operator import pprint @functools.total_ordering class Point: def __init__(self, x, y): self.x x self.y y def __repr__(self): return '({}, {})'.format(self.x, self.y) def __eq__(self, other): return (self.x, self.y) (other.x, other.y) def __gt__(self, other): return (self.x, self.y) > (other.x, other.y) # Create a dataset of Point instances data list(map(Point, cycle(islice(count(), 3)), islice(count(), 7))) print('Data:') pprint.pprint(data, width35) print() # Try to group the unsorted data based on X values print('Grouped, unsorted:') for k, g in groupby(data, operator.attrgetter('x')): print(k, list(g)) print() # Sort the data data.sort() print('Sorted:') pprint.pprint(data, width35) print() # Group the sorted data based on X values print('Grouped, sorted:') for k, g in groupby(data, operator.attrgetter('x')): print(k, list(g)) print()
Входная последовательность должна быть отсортирована по значению ключа, чтобы группировки работали должным образом.
$ python3 itertools_groupby_seq.py Data: [(0, 0), (1, 1), (2, 2), (0, 3), (1, 4), (2, 5), (0, 6)] Grouped, unsorted: 0 [(0, 0)] 1 [(1, 1)] 2 [(2, 2)] 0 [(0, 3)] 1 [(1, 4)] 2 [(2, 5)] 0 [(0, 6)] Sorted: [(0, 0), (0, 3), (0, 6), (1, 1), (1, 4), (2, 2), (2, 5)] Grouped, sorted: 0 [(0, 0), (0, 3), (0, 6)] 1 [(1, 1), (1, 4)] 2 [(2, 2), (2, 5)]
Объединение входов
Функция accumulate ()
обрабатывает итерацию ввода, передавая функции n-й и n + 1-й элемент и вырабатывая возвращаемое значение вместо любого из входных данных. Функция по умолчанию, используемая для объединения двух значений, складывает их, поэтому accumulate ()
можно использовать для получения кумулятивной суммы ряда числовых входных данных.
itertools_accumulate.py
from itertools import * print(list(accumulate(range(5)))) print(list(accumulate('abcde')))
При использовании с последовательностью нецелочисленных значений результаты зависят от того, что означает «сложить» два элемента вместе. Второй пример в этом скрипте показывает, что когда accumulate ()
получает строковый ввод, каждый ответ представляет собой все более длинный префикс этой строки.
$ python3 itertools_accumulate.py [0, 1, 3, 6, 10] ['a', 'ab', 'abc', 'abcd', 'abcde']
Можно комбинировать accumulate ()
с любой другой функцией, которая принимает два входных значения для достижения разных результатов.
itertools_accumulate_custom.py
from itertools import * def f(a, b): print(a, b) return b + a + b print(list(accumulate('abcde', f)))
В этом примере строковые значения комбинируются таким образом, что получается серия (бессмысленных) палиндромов. На каждом этапе вызова f ()
он распечатывает входные значения, переданные ему с помощью accumulate ()
.
$ python3 itertools_accumulate_custom.py a b bab c cbabc d dcbabcd e ['a', 'bab', 'cbabc', 'dcbabcd', 'edcbabcde']
Вложенные циклы for
, которые повторяются по нескольким последовательностям, часто могут быть заменены на product ()
, который создает единственную итерацию, значения которой являются декартовым произведением набора входных значений.
itertools_product.py
from itertools import * import pprint FACE_CARDS ('J', 'Q', 'K', 'A') SUITS ('H', 'D', 'C', 'S') DECK list( product( chain(range(2, 11), FACE_CARDS), SUITS, ) ) for card in DECK: print('{:>2}{}'.format(*card), end' ') if card[1] SUITS[-1]: print()
Значения, создаваемые product ()
, являются кортежами, члены которых берутся из каждого из итераций, переданных в качестве аргументов в порядке их передачи. Первый возвращенный кортеж включает первое значение из каждой итерации. Последняя итерация, переданная в product ()
, обрабатывается первой, затем предпоследней и т. Д. В результате возвращаемые значения располагаются по порядку на основе первой итерации, затем следующей итерации и т. Д.
В этом примере карты отсортированы по достоинству, а затем по масти.
$ python3 itertools_product.py 2H 2D 2C 2S 3H 3D 3C 3S 4H 4D 4C 4S 5H 5D 5C 5S 6H 6D 6C 6S 7H 7D 7C 7S 8H 8D 8C 8S 9H 9D 9C 9S 10H 10D 10C 10S JH JD JC JS QH QD QC QS KH KD KC KS AH AD AC AS
Чтобы изменить порядок карточек, измените порядок аргументов на product ()
.
itertools_product_ordering.py
from itertools import * import pprint FACE_CARDS ('J', 'Q', 'K', 'A') SUITS ('H', 'D', 'C', 'S') DECK list( product( SUITS, chain(range(2, 11), FACE_CARDS), ) ) for card in DECK: print('{:>2}{}'.format(card[1], card[0]), end' ') if card[1] FACE_CARDS[-1]: print()
Цикл печати в этом примере ищет карту туза вместо масти пик, а затем добавляет новую строку, чтобы разбить вывод.
$ python3 itertools_product_ordering.py 2H 3H 4H 5H 6H 7H 8H 9H 10H JH QH KH AH 2D 3D 4D 5D 6D 7D 8D 9D 10D JD QD KD AD 2C 3C 4C 5C 6C 7C 8C 9C 10C JC QC KC AC 2S 3S 4S 5S 6S 7S 8S 9S 10S JS QS KS AS
Чтобы вычислить произведение последовательности на себя, укажите, сколько раз ввод должен быть повторен.
itertools_product_repeat.py
from itertools import * def show(iterable): for i, item in enumerate(iterable, 1): print(item, end' ') if (i % 3) 0: print() print() print('Repeat 2:\n') show(list(product(range(3), repeat2))) print('Repeat 3:\n') show(list(product(range(3), repeat3)))
Поскольку повторение одной итерации похоже на передачу одной и той же итерации несколько раз, каждый кортеж, созданный с помощью product ()
, будет содержать количество элементов, равное счетчику повторений.
$ python3 itertools_product_repeat.py Repeat 2: (0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2) Repeat 3: (0, 0, 0) (0, 0, 1) (0, 0, 2) (0, 1, 0) (0, 1, 1) (0, 1, 2) (0, 2, 0) (0, 2, 1) (0, 2, 2) (1, 0, 0) (1, 0, 1) (1, 0, 2) (1, 1, 0) (1, 1, 1) (1, 1, 2) (1, 2, 0) (1, 2, 1) (1, 2, 2) (2, 0, 0) (2, 0, 1) (2, 0, 2) (2, 1, 0) (2, 1, 1) (2, 1, 2) (2, 2, 0) (2, 2, 1) (2, 2, 2)
Функция permutations ()
создает элементы из итерации ввода, объединенные в возможных перестановках заданной длины. По умолчанию создается полный набор всех перестановок.
itertools_permutations.py
from itertools import * def show(iterable): first None for i, item in enumerate(iterable, 1): if first item[0]: if first is not None: print() first item[0] print(''.join(item), end' ') print() print('All permutations:\n') show(permutations('abcd')) print('\nPairs:\n') show(permutations('abcd', r2))
Используйте аргумент r
, чтобы ограничить длину и количество возвращаемых индивидуальных перестановок.
$ python3 itertools_permutations.py All permutations: abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba dabc dacb dbac dbca dcab dcba Pairs: ab ac ad ba bc bd ca cb cd da db dc
Чтобы ограничить значения уникальными комбинациями, а не перестановками, используйте комбинации ()
. Пока элементы входа уникальны, выходные данные не будут содержать повторяющихся значений.
itertools_combinations.py
from itertools import * def show(iterable): first None for i, item in enumerate(iterable, 1): if first item[0]: if first is not None: print() first item[0] print(''.join(item), end' ') print() print('Unique pairs:\n') show(combinations('abcd', r2))
В отличие от перестановок, аргумент r
для комбинаций ()
является обязательным.
$ python3 itertools_combinations.py Unique pairs: ab ac ad bc bd cd
Хотя комбинация ()
не повторяет отдельные элементы ввода, иногда полезно рассмотреть комбинации, которые действительно включают повторяющиеся элементы. В таких случаях используйте комбинации_with_replacement ()
.
itertools_combinations_with_replacement.py
from itertools import * def show(iterable): first None for i, item in enumerate(iterable, 1): if first item[0]: if first is not None: print() first item[0] print(''.join(item), end' ') print() print('Unique pairs:\n') show(combinations_with_replacement('abcd', r2))
В этом выводе каждый элемент ввода связан с самим собой, а также со всеми другими членами входной последовательности.
$ python3 itertools_combinations_with_replacement.py Unique pairs: aa ab ac ad bb bc bd cc cd dd
Смотрите также
- стандартная библиотечная документация для itertools
- Замечания по переносу Python 2 на 3 для itertools
- The Standard ML Basis Library ) – Библиотека для SML.
- Определение Haskell и стандартных библиотек – спецификация стандартной библиотеки для функционального языка Haskell.
- Clojure . Clojure – это динамический функциональный язык, работающий на виртуальной машине Java.
- tee – инструмент командной строки Unix для разделения одного ввода на несколько идентичных потоков вывода.
- Декартово произведение – математическое определение декартова произведения двух последовательностей.