Коробка с питоном
543 subscribers
45 photos
118 links
Заметки от Python-разработчика: сниппеты, обзоры пакетов, новости и другая полезная информация.

Автор: @kiriharu
Download Telegram
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :)
Сам модуль itertools это набор из эффективных и быстрых по памяти инструментов, возвращающие итераторы. Сами по себе итераторы можно комбинировать с такими функциями как map(), list() лямбдами или же использовать их с помощью цикла for.

А сегодня мы начнем c самых простых - c бесконечных итераторов. Всего по документации их три: count(), cycle() и repeat().
Ниже я буду указывать аргументы по умолчанию и возможные передаваемые типы как тайп-хинты. Надеюсь ни у кого не возникнет проблем с пониманием.

#itertools #std
Коробка с питоном
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :) Сам модуль itertools это набор из эффективных и быстрых по памяти…
count(start: int | float = 0, step: int | float = 1) -> Iterator[int | float]

Создает итератор, который возвращает равномерно распределенные значения, начиная с числа, указанного в аргументе start. Само значение-шаг указывается в переменной step:

>>> first = count(10, 2)
>>> next(first)
10
>>> next(first)
12
>>> next(first)
14
>>> second = count(0.1, 0.1)
>>> next(second)
0.1
>>> next(second)
0.2
>>> next(second)
0.30000000000000004
>>> next(second)
0.4

Небольшая загадка для вас - почему вместо 0.3 получили такое число? Пишите в комменты :)

#itertools #std
Коробка с питоном
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :) Сам модуль itertools это набор из эффективных и быстрых по памяти…
cycle(p: Iterable) -> Iterator

Создает итератор, который возвращает элементы из переданного итератора поштучно и сохраняет их. После того как элементы закончились, он начинает возвращать сохранённые элементы и так до тех пор, пока мы сами не прервем его работу:

>>> first = cycle([1, 2])
>>> next(first)
1
>>> next(first)
2
>>> next(first)
1
>>> next(first)
2

Например, вот так при помощи функций islice и cycle мы можем сгенерировать список из 20 элементов из необходимой нам коллекции:

>>> list(islice(cycle([1, 2, 3, 4]), None, 20))
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]

#itertools #std
Коробка с питоном
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :) Сам модуль itertools это набор из эффективных и быстрых по памяти…
repeat(object: T, times: Optional[int] = None) -> Iterable[T]

Создает итератор который возвращает объект снова и снова. Можно указать параметр times, который вернет объект заданное количество раз и завершит работу итератора:

>>> first = repeat("hehe", 3)
>>> next(first)
'hehe'
>>> next(first)
'hehe'
>>> next(first)
'hehe'
>>> next(first)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Очень часто repeat используют чтобы по быстрому сгенерировать коллекцию из элементов:

>>> list(repeat(10, 10))
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]

#itertools #std
Если обычного itertools вам мало, то можно использовать more-itertools.

Эта библиотека добавляет огромное количество функций для работы с итераторами. На практике всеми ими не всегда пользуются, поэтому я выделю некоторые из тех, которые сам использую часто.

Например, вот так можно разделить список на 3 части:

data = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh"]
[list(l) for l in divide(3, data)]
# [['first', 'second', 'third'], ['fourth', 'fifth'], ['sixth', 'seventh']]

Вот еще задача. Надо разделить список с элементами по определенному условию. В этом нам поможет bucket:

class Cat:
pass
class Dog:
pass
shapes = [Cat(), Dog(), Cat(), Dog(), Cat(), Cat()]
result = more_itertools.bucket(shapes, key=lambda x: type(x))
len(list(result[Cat])) # 4
len(list(result[Dog])) # 2

По итогу кошки и собаки будут разделены по своему типу на 2 генератора.

Решим самую частую проблему - перевести список с несколькими уровнями вложенностями в "плоский" список:

iterable = [(1, 2), ([3, 4], [[5], [6]])]
list(more_itertools.collapse(iterable)) #[1, 2, 3, 4, 5, 6]

А если в плоский список нам нужно вытащить только элементы с первым уровнем вложенности?

list(more_itertools.collapse(iterable, levels=1)) # [1, 2, [3, 4], [[5], [6]]]

А вот так мы можем посмотреть, все ли элементы в коллекции уникальные:

more_itertools.all_unique([1, 2, 3, 4]) # True
more_itertools.all_unique([1, 2, 1, 4]) # False

Библиотека решает очень много типовых проблем, поэтому если научиться ей пользоваться - она сэкономит очень много времени. Возможно, я еще буду писать какие-то рецепты с ней, но это не точно 🌚...

PyPI | Документация

#more_itertools #itertools #библиотека #рецепт
Коробка с питоном
Если обычного itertools вам мало, то можно использовать more-itertools. Эта библиотека добавляет огромное количество функций для работы с итераторами. На практике всеми ими не всегда пользуются, поэтому я выделю некоторые из тех, которые сам использую часто.…
На сегодня расскажу ещё пару рецептов с more_itertools.

1) map_if работает как обычный map, но применяет функцию на элемент только если оно попадает под условие. Например, вот так мы можем возвести в квадрат только те числа, которые делятся на 2 нацело:

example = [1, 2, 3, 4, 5, 6, 7, 8]
list(map_if(example, lambda x: x % 2 == 0, lambda x: x * x)) # [1, 4, 3, 16, 5, 36, 7, 64]


2) Получить последний элемент можно при помощи last. Возникает вопрос а зачем он существует, если можно указать sequence[-1]? Ответом является то, что last позволяет указать, что ему возвращать, если элементов в коллекции нет:

last([1, 2, 3]) # Очевидно получим 3 
last([], 0) # Список пустой, но получим 0
[][-1] # Получим IndexError


Ещё есть first - как понятно из названия, он получает первый элемент.

3) map_except тоже работает как map, но умеет игнорировать ошибки. Например, мы хотим получить только те элементы, которые получилось привести к целому числу:

example = [1, "1", "2", "test", "three", object, 4.0]
list(map_except(int, example, ValueError, TypeError)) # [1, 1, 2, 4]


4) Ну и в конце про take - он просто берет N элементов из итерируемого объекта:

example = range(10)
take(3, example) # [0, 1, 2]
take(20, example) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - если больше, возьмет доступные


#itertools #more_itertools #библиотека #рецепт