Итератор — объект в Python, который является объектным представлением потока данных. Говорят, что объект реализует протокол итератора, если объект:
__iter__()
, который возвращает объект с методом __next__()
,__next__()
вызывает исключение StopIteration
, когда поток данных закончился.О таком объекте говоря, что он итерируемый (iterable
). Итерируемый объект возвращает через __iter__()
итератор (iterator
).
Например, список итерируемый. У него можно найти __iter__()
.
# Список итерируемый
import inspect
inspect.getmembers([1,2,3])
Для работы с итераторами в Python есть две вспомогательные функции: iter
и next
. Первая возвращает итератор, вторая — следующее значение итератора.
# Вспомогательные функции для работы с итераторами
lst = [1,2,3]
itr = iter(lst)
next(itr), next(itr), next(itr)
# Если поток закончился, то next вызовет исключение (должн быть ошибка)
next(itr)
Опишем объект, который возвращает целые числа от value
до «бесконечности» с шагом step
. Здесь сам объект имеет метод __next__()
, поэтому в __iter__()
он возвращает сам себя. При этом объект и итерируемый и итератор одновременно.
# Пример итерируемого объекта
class Counts:
def __init__(self, start=0, step=1):
self.value = start
self.step = step
def __iter__(self):
return self
def __next__(self):
self.value += self.step
return self.value
s = Counts(step=2)
next(s), next(s), next(s)
NB! Итератор Counts
уже реализован в модуле itertools.
Генератор — «синтаксический сахар» для итератора. С точки зрения синтаксиса, генератор — функция с ключевым словом yield
внутри.
Чтобы сгенерировать значение, надо в next
передать генератор. Внутри происходит следующее:
yield val
,val
.Для примера перепишем итератор Counts
как генератор.
def counts(value=0, step=1):
while 1:
value += step
yield value
g = counts(step=2)
type(g), next(g), next(g), next(g)
Синтаксически конструкции похожи.
# Включение в список
lstcomp = [x*x for x in range(10)]
type(lstcomp), lstcomp
# Выражение-генератор
genexpr = (x*x for x in range(10))
type(genexpr), next(genexpr), next(genexpr), next(genexpr)
Генератор — это процедура получения значений последовательности данных. Поэтому размер генератора меньше, чем непосредственно списка значений последовательности данных.
# Размер списка vs размер генератора
from sys import getsizeof
data_a = [i for i in range(1000000)]
data_b = (i for i in range(1000000))
getsizeof(data_a), getsizeof(data_b)
Генерация списка с помощью генераторов работает медленнее из-за затрат на вызов функции:
%timeit [i for i in range(1000000)]
%timeit list(i for i in range(1000000))
Python — мультипарадигменный язык программирования. На нем можно писать в
Программа в функциональном стиле выглядит как последовательное применение функций к входным данным. Результат каждой их этих функций зависит только от её аргументов. Такие функции называют чистыми функциями. У нечистых функции есть побочные эффекты).
Обычно, если язык программирования поддерживает функциональный стиль, то в нём реализованы:
Лямбда-функция определеяются с помощью ключевого слова lambda
. Объявление лямбда-функции аналогично объявлению функции с помощью def
, только
return
.# Лябда-функция
type(lambda x: x*x)
# Применение лябда-функции
(lambda x, y: x+y)(2,3)
# Применение лябда-функции как обычной функции
f=lambda x, y: x+y
f(2,3)
Например:
# Лябда-функция в замыканиях
def gen_mul(a):
return lambda b: a*b
double=gen_mul(2)
double(3)
NB! Если беглым взглядом непонятно что делает лямбда-функция, то лучше ее реализовтаь как обычную функцию.
Функции map
и filter
встроены в язык, а reduce
находится в модуле functools
.
Функция map
выполняет отображение одной последовательности в другую, применяя к каждом элементу одну и ту же фукнцию.
Условно map
работает так:
[a,b,c] -> [f(a),f(b),f(c)]
# Пример: взять для каждого элемента квадрат
map(lambda x: x*x, [1,2,3])
map
возвращает итератор. Удобно преобразовать к списку:
# Пример: взять для каждого элемента квадрат
list(map(lambda x: x*x, [1,2,3]))
Функция filter
выполняет фильтрацию входной последовательности, оставляя только те элементы, которые удовлетворяют критерию.
Условно filter
работает так:
[a,b,c] -> {x != b} -> [a,c]
# Пример: оставить элементы только больше 2
list(filter(lambda x: x>2, [1,2,3]))
Функция reduce
выполняет сворачивание входной последовательности к одному значению, применяя последовательно функцию.
Условно reduce
работает так:
[a,b,c] -> f[f[a,b],c]
# Пример 1: сумма элементов последовательности
from functools import reduce
reduce(lambda x,y: x+y, [1,2,3], 0)
# Пример 2: скалярное произведение
from functools import reduce
a = [1,2,3]
b = [6,0,4]
reduce(lambda x,y: x+y[0]*y[1], zip(a,b), 0)
Здесь zip
— вспомогательная функция высшего порядка, которая собирает из двух последовательностей одну по такому правилу:
zip([a1,a2,a3],[b1,b2,b3]) -> [(a1,b1), (a2,b2), (a3,b3)]
%load_ext watermark
%watermark -d -u -v -iv