Модуль itertools
Модуль itertools
містить ряд загальновживаних ітераторів, а також функції для комбінування декількох ітераторів. У цьому розділі буде представлено вміст модуля через невеликі приклади.
Функції модуля поділяються на кілька широких категорій:
Створення нових ітераторів
itertools.count(start, step)
повертає нескінченний потік рівномірно розподілених значень. Ви можете за бажанням вказати початкове число start
, яке за замовчуванням дорівнює 0, і інтервал між числами step
, який за замовчуванням дорівнює 1:
itertools.count() =>
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
itertools.count(10) =>
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...
itertools.count(10, 5) =>
10, 15, 20, 25, 30, 35, 40, 45, 50, 55, ...
itertools.cycle(iter)
зберігає копію вмісту наданого ітерабельного об'єкта і повертає новий
ітератор, який повертає його елементи з першого до останнього. Новий ітератор
буде повторювати ці елементи нескінченно:
itertools.cycle([1, 2, 3, 4, 5]) =>
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
itertools.repeat(elem, [n])
повертає наданий елемент n разів або повертає
елемент нескінченно, якщо n не вказано:
itertools.repeat('abc') =>
abc, abc, abc, abc, abc, abc, abc, abc, abc, abc, ...
itertools.repeat('abc', 5) =>
abc, abc, abc, abc, abc
itertools.chain(iterA, iterB, ...)
приймає довільну кількість ітерабельних об'єктів як вхідні дані і
повертає всі елементи першого ітератора, потім всі елементи
другого, і так далі, поки всі ітерабельні об'єкти не будуть вичерпані:
itertools.chain(['a', 'b', 'c'], (1, 2, 3)) =>
a, b, c, 1, 2, 3
itertools.islice(iter, [start], stop, [step])
повертає потік, який є зрізом ітератора. З одним аргументом stop він поверне перші stop елементи. Якщо ви вкажете початковий індекс, ви отримаєте stop-start елементи, і якщо ви вкажете значення для step, елементи будуть пропущені відповідно. На відміну від зрізання рядків і списків у Python, тут не можна використовувати від'ємні значення для start, stop або step:
itertools.islice(range(10), 8) =>
0, 1, 2, 3, 4, 5, 6, 7
itertools.islice(range(10), 2, 8) =>
2, 3, 4, 5, 6, 7
itertools.islice(range(10), 2, 8, 2) =>
2, 4, 6
itertools.tee(iter, [n])
реплікує ітератор; повертає n незалежних
ітераторів, які всі повернуть вміст вихідного ітератора. Якщо
ви не вкажете значення для n, за замовчуванням це 2. Реплікація
ітераторів вимагає збереження деяких даних з вихідного ітератора,
тому це може споживати значну пам'ять, якщо ітератор великий і один
з нових ітераторів використовується більше, ніж інші:
itertools.tee( itertools.count() ) =>
iterA, iterB
де iterA ->
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
і iterB ->
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
Виклик функцій на елементах
Модуль operator
містить набір функцій, що відповідають операторам Python. Деякі приклади:
operator.add(a, b)
(додає два значення), operator.ne(a, b)
(те ж саме, що a != b
), і operator.attrgetter('id')
(повертає callable, що отримує атрибут .id
).
itertools.starmap(func, iter)
передбачає, що ітерабельний об'єкт поверне потік кортежів, і викликає func, використовуючи ці кортежі як аргументи:
itertools.starmap(os.path.join,
[('/bin', 'python'), ('/usr', 'bin', 'java'),
('/usr', 'bin', 'perl'), ('/usr', 'bin', 'ruby')])
=>
/bin/python, /usr/bin/java, /usr/bin/perl, /usr/bin/ruby
Вибір елементів
Інша група функцій вибирає підмножину елементів ітератора на основі предиката.
itertools.filterfalse(predicate, iter)
є протилежністю filter
,
повертаючи всі елементи, для яких предикат повертає false:
itertools.filterfalse(is_even, itertools.count()) =>
1, 3, 5, 7, 9, 11, 13, 15, ...
itertools.takewhile(predicate, iter)
повертає елементи, поки предикат повертає True
. Як тільки предикат поверне false, ітератор сигналізує про кінець
своїх результатів:
import itertools
def less_than_10(x):
return x < 10
def is_even(x):
return (x % 2) == 0
print(list(itertools.takewhile(less_than_10, itertools.count()))) # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
list(itertools.takewhile(is_even, itertools.count())) # 0
itertools.dropwhile(predicate, iter)
відкидає елементи, поки предикат повертає True
, і
тоді повертає решту результатів ітерабельного об'єкта:
itertools.dropwhile(less_than_10, itertools.count()) =>
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...
itertools.dropwhile(is_even, itertools.count()) =>
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
itertools.compress(data, selectors)
приймає два ітератори і повертає тільки ті елементи
data, для яких відповідний елемент selectors є істинним, зупиняючись, коли будь-який з них буде вичерпаний:
import itertools
list(itertools.compress([1, 2, 3, 4, 5], [True, True, False, False, True])) # 1, 2, 5
Комбінаторні функції
Функція itertools.combinations(iterable, r)
повертає ітератор, що дає всі можливі r-кортежеві
комбінації елементів, які містяться в iterable:
itertools.combinations([1, 2, 3, 4, 5], 2) =>
(1, 2), (1, 3), (1, 4), (1, 5),
(2, 3), (2, 4), (2, 5),
(3, 4), (3, 5),
(4, 5)
itertools.combinations([1, 2, 3, 4, 5], 3) =>
(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5),
(2, 3, 4), (2, 3, 5), (2, 4, 5),
(3, 4, 5)
Елементи всередині кожного кортежу залишаються в тому ж порядку, в якому
iterable їх повернув. Наприклад, число 1 завжди перед 2, 3, 4 або 5
в наведених прикладах. Схожа функція,
itertools.permutations(iterable, r=None)
, знімає це обмеження на порядок, повертаючи всі
можливі розташування довжини r:
itertools.permutations([1, 2, 3, 4, 5], 2) =>
(1, 2), (1, 3), (1, 4), (1, 5),
(2, 1), (2, 3), (2, 4), (2, 5),
(3, 1), (3, 2), (3, 4), (3, 5),
(4, 1), (4, 2), (4, 3), (4, 5),
(5, 1), (5, 2), (5, 3), (5, 4)
itertools.permutations([1, 2, 3, 4, 5]) =>
(1, 2, 3, 4, 5), (1, 2, 3, 5, 4), (1, 2, 4, 3, 5),
...
(5, 4, 3, 2, 1)
Якщо ви не вкажете значення для r, буде використана довжина ітерабельного об'єкта, що означає, що всі елементи будуть переставлені.
Зверніть увагу, що ці функції генерують всі можливі комбінації за позицією і не вимагають, щоб вміст iterable був унікальним:
itertools.permutations('aba', 3) =>
('a', 'b', 'a'), ('a', 'a', 'b'), ('b', 'a', 'a'),
('b', 'a', 'a'), ('a', 'a', 'b'), ('a', 'b', 'a')
Ідентичний кортеж ('a', 'a', 'b')
з'являється двічі, але два 'a'
рядки походять з різних позицій.
Функція
itertools.combinations_with_replacement(iterable, r)
послаблює інше обмеження: елементи можуть
повторюватися всередині одного кортежу. Концептуально елемент вибирається для
першої позиції кожного кортежу, а потім замінюється перед вибором
другого елемента:
itertools.combinations_with_replacement([1, 2, 3, 4, 5], 2) =>
(1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
(2, 2), (2, 3), (2, 4), (2, 5),
(3, 3), (3, 4), (3, 5),
(4, 4), (4, 5),
(5, 5)
Групування елементів
Остання функція, яку я обговорю, itertools.groupby(iter, key_func=None)
, є найбільш
складною. key_func(elem)
це функція, яка може обчислити значення ключа
для кожного елемента, що повертається ітерабельним об'єктом. Якщо ви не
вкажете функцію ключа, ключем буде просто кожен елемент сам по собі.
itertools.groupby()
збирає всі
послідовні елементи з основного ітерабельного об'єкта, які мають той
самий ключ, і повертає потік з 2-кортежів, що містять значення ключа і
ітератор для елементів з цим ключем.
city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'),
('Anchorage', 'AK'), ('Nome', 'AK'),
('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'),
...
]
def get_state(city_state):
return city_state[1]
itertools.groupby(city_list, get_state) =>
('AL', iterator-1),
('AK', iterator-2),
('AZ', iterator-3), ...
де
iterator-1 =>
('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL')
iterator-2 =>
('Anchorage', 'AK'), ('Nome', 'AK')
iterator-3 =>
('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ')
itertools.groupby()
припускає, що
вміст основного ітерабельного об'єкта вже буде відсортовано на основі ключа.
Зверніть увагу, що повернені ітератори також використовують основний ітерабельний об'єкт, тому
ви повинні спожити результати ітератора-1 перед запитом
ітератора-2 і його відповідного ключа.
Текст на цій сторінці є перекладом "Functional Programming HOWTO", автор: A. M. Kuchling. Інформація про копірайт: History and License.