Python 3.3_2022_Итераторы и генераторы

Содержание

Слайд 2

Во многих современных языках программирования используют такие сущности как итераторы.
Основное

Во многих современных языках программирования используют такие сущности как итераторы. Основное их
их назначение – это упрощение навигации по элементам объекта, который, как правило, представляет собой некоторую коллекцию (список, словарь и т.п.).

Слайд 3

Определения

Итерируемый объект – это объект, который позволяет поочередно обойти свои элементы и

Определения Итерируемый объект – это объект, который позволяет поочередно обойти свои элементы
может быть преобразован к итератору.
Итератор – это объект, который поддерживает функцию next() для перехода к следующему элементу коллекции.

Слайд 4

пример

Когда вы создаёте список (list) вы можете считывать его элементы по одному

пример Когда вы создаёте список (list) вы можете считывать его элементы по
— это называется итерацией.
lst = [1, 2, 3]
for i in lst:
print(i)
1
2
3
Lst — итерируемый объект (iterable)

Слайд 5

for

Основное место использования итераторов – это цикл for.
Если вы перебираете

for Основное место использования итераторов – это цикл for. Если вы перебираете
элементы в некотором списке или символы в строке с помощью цикла for, то ,фактически, это означает, что при каждой итерации цикла происходит обращение к итератору, содержащемуся в строке/списке, с требованием выдать следующий элемент, если элементов в объекте больше нет, то итератор генерирует исключение, обрабатываемое в рамках цикла for незаметно для пользователя.

Слайд 6

Итерирумые объекты достаточно удобны потому что вы можете считывать из них столько

Итерирумые объекты достаточно удобны потому что вы можете считывать из них столько
данных, сколько вам необходимо, но при этом вы храните все значения последовательности в памяти и это не всегда приемлемо, особенно если вы имеете достаточно большие последовательности.

Слайд 7

iter() и next()

Объекты, элементы которых можно перебирать в цикле for, содержат

iter() и next() Объекты, элементы которых можно перебирать в цикле for, содержат
в себе объект итератор, для того, чтобы его получить необходимо использовать функцию iter(), а для извлечения следующего элемента из итератора – функцию next().

Слайд 8

пример

num_list = [1, 2, 3, 4, 5]
for i in num_list:
print(i)
1
2
3
4
5

пример num_list = [1, 2, 3, 4, 5] for i in num_list:

Слайд 9

itr = iter(num_list)
print(next(itr))
1
print(next(itr))
2
print(next(itr))
3
print(next(itr))
4
print(next(itr))
5
print(next(itr))
Traceback (most recent call last):
File "", line 1, in

itr = iter(num_list) print(next(itr)) 1 print(next(itr)) 2 print(next(itr)) 3 print(next(itr)) 4 print(next(itr))

print(next(itr))

Как видно из примера вызов функции next(itr) каждый раз возвращает следующий элемент из списка, а когда эти элементы заканчиваются, генерируется исключение StopIteration.

Слайд 10

Генератор

Генератор – это итератор, элементы которого можно перебирать (итерировать) только один раз.
Любая

Генератор Генератор – это итератор, элементы которого можно перебирать (итерировать) только один
функция в Python, в теле которой встречается ключевое слово yield, называется генераторной функцией — при вызове она возвращает объект-генератор.
Вместо ключевого слова return в генераторе используется yield.

Слайд 11

yield

При первом исполнении кода тела функции код будет выполнен с начала и

yield При первом исполнении кода тела функции код будет выполнен с начала
до первого встретившегося оператора yield. После этого будет возвращено первое значение и выполнение тела функции опять приостановлено.
Запрос следующего значения у генератора во время итерации заставит код тела функции выполняться дальше (с предыдущего yield’а), пока не встретится следующий yield. Генератор считается «закончившимся» в случае если при очередном исполнении кода тела функции не было встречено ни одного оператора yield.

Слайд 12

Функции-генераторы – это функции, которые возвращают значение и затем могут продолжить работу

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

Слайд 13

Во многих отношениях, функция-генератор выглядит очень похоже на обычную функцию. Основное отличие

Во многих отношениях, функция-генератор выглядит очень похоже на обычную функцию. Основное отличие
в том, что когда эта функция компилируется, она становится объектом, который поддерживает протокол итераций.
Это значит, что когда такая функция вызывается в Вашем коде, она не просто возвращает значение и завершает работу.
Вместо этого, функция-генератор ставит своё выполнение на паузу, и возобновляет выполнение с последней точки генерации значений.
Основное преимущество такого подхода в том, что вместо необходимости сразу вычислить всю серию значений, генератор генерирует одно значение и ставит выполнение на паузу, ожидая дальнейших инструкций.
Такая особенность работы называется state suspension.

Слайд 14

range()

Например, функция range() не создает весь список в памяти от начала до

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

Слайд 15

пример

Функция, которая возводит числа в куб
def create_cubes(n):
result = []
for x

пример Функция, которая возводит числа в куб def create_cubes(n): result = []
in range (n):
result.append(x**3)
return result
здесь мы храним в памяти весь список

Слайд 16

Функция-генератор, которая возводит числа в куб
def gencubes(n):
for x in range(n):
yield

Функция-генератор, которая возводит числа в куб def gencubes(n): for x in range(n):
x**3
Здесь каждый раз получаем лишь одно значение, всю последовательность одновременно в списке не храним, используем память более эффективно. Особенно заметно при работе с Big Data
Чтобы получить результат в виде списка используем list(gencubes(10))

Слайд 17

аналогично

for x in gencubes(10):
print(x)
0
1
8
27
64
125
216
343
512
729

не хранит в памяти список, каждый раз выводит

аналогично for x in gencubes(10): print(x) 0 1 8 27 64 125
лишь одно значение

Слайд 18

функция для получения чисел Фибоначчи

def genfibon(n):
"""
Generate a fibonnaci sequence up

функция для получения чисел Фибоначчи def genfibon(n): """ Generate a fibonnaci sequence
to n
"""
a = 1
b = 1
for i in range(n):
yield a
a,b = b,a+b

a – очередное число
b - предыдущее число
yield возвращает очередное значение

Слайд 19

пример

for num in genfibon(10):
print(num)
1
1
2
3
5
8
13
21
34
55

пример for num in genfibon(10): print(num) 1 1 2 3 5 8 13 21 34 55

Слайд 20

обычная функция

храним в памяти весь список
def fibon(n):
a = 1
b =

обычная функция храним в памяти весь список def fibon(n): a = 1
1
output = []
for i in range(n):
output.append(a)
a,b = b,a+b
return output

Слайд 21

Если мы укажем больше значение n (например 100000), вторая функция будет хранить

Если мы укажем больше значение n (например 100000), вторая функция будет хранить
каждое из результирующих значений, хотя в нашем случае нам только нужен предыдущий результат, чтобы вычислить следующее значение

Слайд 22

Выражение - генератор

Генераторы выражений предназначены для компактного и удобного способа генерации коллекций

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

Слайд 23

Преимущества использования генераторов выражений
Более короткий и удобный синтаксис, чем генерация в обычном

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

Слайд 24

классификация

выражение-генератор (generator expression) — выражение в круглых скобках которое выдает на каждой

классификация выражение-генератор (generator expression) — выражение в круглых скобках которое выдает на
итерации новый элемент по правилам.
генератор коллекции — обобщенное название для генератора списка (list comprehension), генератора словаря (dictionary comprehension) и генератора множества (set comprehension)

Слайд 25

List comprehensions

Генераторы списков предназначены для удобной обработки списков, к которой можно отнести

List comprehensions Генераторы списков предназначены для удобной обработки списков, к которой можно
и создание новых списков, и модификацию существующих.

Слайд 26

Генератор списков

Для создания списка, заполненного одинаковыми элементами, можно использовать оператор повторения

Генератор списков Для создания списка, заполненного одинаковыми элементами, можно использовать оператор повторения
списка, например:
A = [0] * n
Общий вид генератора следующий:
[выражение for переменная in список]

Слайд 27

[выражение for переменная in список]

где
переменная — идентификатор некоторой переменной,

[выражение for переменная in список] где переменная — идентификатор некоторой переменной, список

список — список значений, который принимает данная переменная (как правило, полученный при помощи функции range),
выражение — некоторое выражение, которым будут заполнены элементы списка, как правило, зависящее от использованной в генераторе переменной.
Вот несколько примеров использования генераторов.
Создать список, состоящий из n нулей
A = [0 for i in range(n)]

Слайд 28

Генераторы списков

Создать список, заполненный квадратами целых чисел можно так:
A = [i

Генераторы списков Создать список, заполненный квадратами целых чисел можно так: A =
** 2 for i in range(n)]
Если нужно заполнить список квадратами чисел от 1 до n, то можно изменить параметры функции range на range(1, n + 1):
A = [i ** 2 for i in range(1, n + 1)]

Слайд 29

Генератор списков

Вот так можно получить список, заполненный случайными числами от 1

Генератор списков Вот так можно получить список, заполненный случайными числами от 1
до 9 (используя функцию randint из модуля random):
(про работу с модулями подробности позже)
A = [randint(1, 9) for i in range(n)]
А в этом примере список будет состоять из строк, считанных со стандартного ввода: сначала нужно ввести число элементов списка (это значение будет использовано в качестве аргумента функции range), потом — заданное количество строк:
A = [input() for i in range(int(input()))]

Слайд 30

Генератор списков

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b

Генератор списков list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
= [x for x in list_a if x % 2 == 0]
print(list_b)
[-2, 0, 2, 4]
list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x for x in list_a if x % 2 == 0 and x > 0]
# берем те x, которые одновременно четные и больше нуля
print(list_b)
[2, 4]

Слайд 31

Генератор списков

Выражение выполняется независимо на каждой итерации, обрабатывая каждый элемент индивидуально.
Можно использовать

Генератор списков Выражение выполняется независимо на каждой итерации, обрабатывая каждый элемент индивидуально.
условия:
list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x if x < 0 else x**2 for x in list_a]
# Если x-отрицательное - берем x, в остальных случаях - берем квадрат x
print(list_b)
[-2, -1, 0, 1, 4, 9, 16, 25]

Слайд 32

Генератор списков

>>> c = [c * 3 for c in 'list']
>>> c
['lll',

Генератор списков >>> c = [c * 3 for c in 'list']
'iii', 'sss', 'ttt']
---------------
>>> c = [c * 3 for c in 'list' if c != 'i']
>>> c
['lll', 'sss', 'ttt']
----------------------
>>> c = [c + d for c in 'list' if c != 'i' for d in 'spam' if d != 'a']
>>> c
['ls', 'lp', 'lm', 'ss', 'sp', 'sm', 'ts', 'tp', 'tm']

Слайд 33

сравнение

numbs = [1, 2, 3, 4, 5]
result = []
for x in numbs:
if

сравнение numbs = [1, 2, 3, 4, 5] result = [] for
x > 3:
y = x * x
result.append(y)
numbs = [1, 2, 3, 4, 5]
result = [x * x for x in numbs if x > 3]

Слайд 34

Генератор множества (set comprehension)

list_a = [-2, -1, 0, 1, 2, 3, 4,

Генератор множества (set comprehension) list_a = [-2, -1, 0, 1, 2, 3,
5]
my_set= {i for i in list_a}
print(my_set)
{0, 1, 2, 3, 4, 5, -1, -2} - порядок случаен

Слайд 35

Генератор словаря (dictionary comprehension) – переворачиваем словарь

dict_abc = {'a': 1, 'b': 2,

Генератор словаря (dictionary comprehension) – переворачиваем словарь dict_abc = {'a': 1, 'b':
'c': 3, 'd': 3}
dict_123 = {v: k for k, v in dict_abc.items()}
print(dict_123)
{1: 'a', 2: 'b', 3: 'd'}
Обратите внимание, мы потеряли "с"! Так как значения были одинаковы, то когда они стали ключами, только последнее значение сохранилось.

Слайд 36

Генератор словаря

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
dict_a =

Генератор словаря list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
{x: x**2 for x in list_a}
print(dict_a)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, -2: 4, -1: 1, 5: 25}
dict_gen = ((x, x ** 2) for x in list_a)
генератор-выражения для словаря

Слайд 37

Выражение-генератор

Выражения-генераторы (generator expressions) доступны, начиная с Python 2.4. Основное их отличие от

Выражение-генератор Выражения-генераторы (generator expressions) доступны, начиная с Python 2.4. Основное их отличие
генераторов коллекций в том, что они выдают элемент по-одному, не загружая в память сразу всю коллекцию.
Если мы создаем большую структуру данных без использования генератора, то она загружается в память целиком, соответственно, это увеличивает расход памяти приложением, а в крайних случаях памяти может просто не хватить.
В случае использования выражения-генератора, такого не происходит, так как элементы создаются по-одному, в момент обращения.

Слайд 38

синтаксис

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
my_gen = (i

синтаксис list_a = [-2, -1, 0, 1, 2, 3, 4, 5] my_gen
for i in list_a) # выражение-генератор
print(next(my_gen)) # -2 - получаем очередной элемент генератора
print(next(my_gen)) # -1 - получаем очередной элемент генератора

Слайд 39

Выражение-генератор

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
my_sum = sum(i

Выражение-генератор list_a = [-2, -1, 0, 1, 2, 3, 4, 5] my_sum
for i in list_a)
# my_sum = sum((i for i in list_a)) # так тоже можно
print(my_sum) # 12

Слайд 40

Выражение-генератор

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
my_gen = (i

Выражение-генератор list_a = [-2, -1, 0, 1, 2, 3, 4, 5] my_gen
for i in list_a)
print(sum(my_gen)) # 12
print(sum(my_gen)) # 0
Обратите внимание, что после прохождения по выражению-генератору оно остается пустым!

Слайд 41

Практика

Создать генератор списка из исходного
берет только четные значения, отрицательные возводит в куб,

Практика Создать генератор списка из исходного берет только четные значения, отрицательные возводит
остальные в квадрат
считает длину строк для списка из строк
список квадратов четных чисел
только положительные, кратные 5, отрицательные заменить на 0
из строки – только гласные буквы
Создать генератор словаря, значение равно квадрат ключа