Оглавление
Функциональное программирование и встроенные функции
sorted(iterable, *, key=None, reverse=False)
Функциональное программирование и встроенные функции
Функциональное программирование (ФП) разделяет задачу на некоторую последовательность функций. В ФП функции должны принимать входные данные, обрабатывать их и возвращать выходные данные, при этом не должны иметь какого-либо внутреннего состояния, которое будет влиять на результат. Иными словами, функция должна всегда возвращать одинаковый результат при одинаковых входных данных. Это — фундаментальная концепция ФП.
Лямбды
Лямбда в Python — еще один способ определения функции. Вот базовый синтаксис лямбда-функции в Python:
foo = lambda arguments: expression
Синтаксис лямбда-функции с одним аргументом:
f = lambda x: x * x
Синтаксис лямбда-функции с позиционными аргументами:
foo = lambda *args: args
foo(1, 2)
Синтаксис лямбда-функции с непозиционными аргументами:
foo = lambda **kw: kw
foo(var1=1, var2=2)
Чему эквивалентны лямбды?
foo = lambda v: v + 1
⇐>
def foo(v):
return v + 1
map(function, iterable, ...)
Вернет итератор, который применяет функцию к каждому элементу итерируемого объекта, например списка.
list(map(lambda v: v + 1, [1, 2, 3]))
>>> [2, 3, 4]
filter(foo, iterable)
Создает итератор из тех элементов, для которых функция foo возвращает true. iterable может быть либо списком, либо объектом, который поддерживает итерацию. Обратите внимание, что filter(function, iterable) эквивалентно выражению генератора (item for item in iterable if function(item)).
arr = [1,2,3,4,5]
list(filter(lambda v: v > 3, arr))
>>> [4, 5]
enumerate(iterable, start=0)
Создает итератор из кортежей, которые состоят из индекса элемента в последовательности и самого элемента.
arr = [1, 2, 3]
it = enumerate(arr)
list(it)
>>> [(0, 1), (1, 2), (2, 3)]
Функция enumerate эквивалентна генератору:
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
zip(*iterables, strict=False)
Возвращает итератор из кортежей, где i-й кортеж содержит i-й элемент из каждого переданного итератора.
ids = [10, 20]
names = ['Александр', 'Сергей']
res = zip(ids, names, names)
list(res)
>>> [(10, 'Александр', 'Александр'), (20, 'Сергей', 'Сергей')]
Если **strict = False** и итераторы имеют разную длину, то длина результирующего итератора будет равна длине минимального итератора. Если **strict = True** и итераторы имеют разную длину, то возникнет ошибка.
sorted(iterable, *, key=None, reverse=False)
Отсортирует элементы из iterable и вернет новый список. Важно отметить, что функции выше создавали итераторы, а sorted сразу получает все элементы итератора, сортирует их и возвращает список. **key** — функция, которая извлекает ключ сравнения. Если не указывать key, то sorted будет сравнивать объекты из итератора напрямую, то есть так: el1 > el2, в этом случае эти объекты должны поддерживать операции сравнения. Если указать key, то перед сравнением будет выполнена функция key для извлечения ключа сравнения, то есть key(el1) > key(el2). Key может быть полезен, например, когда элементы итератора являются кортежами. **Reverse** отвечает за порядок в последовательности. Если reverse = False, то происходит сортировка по возрастанию, если reverse = True — сортировка по убыванию.
sum(iterable)
Суммирует все элементы последовательности. Важно, чтобы каждый элемент последовательности поддерживал операцию сложения. Все числовые переменные по умолчанию поддерживают сложение.
min/max(iterable)
Получает минимальный (min)/максимальный (max) элемент из последовательности. Важно, чтобы каждый элемент поддерживал операции сравнения (> и/или <). Все числовые переменные по умолчанию поддерживают операции сравнения. Функция min/max принимает аргумент key. key — функция, которая извлекает ключ сравнения. Если указать key, то перед сравнением будет выполнена функция key для извлечения ключа сравнения, то есть key(el1) > key(el2).
min([(1, 2), (2, 1)], key=lambda v: v[1])
max([(1, 2), (2, 1)], key=lambda v: v[1])
Collections
Namedtuple
Именованные кортежи (namedtuple) — это подкласс кортежей в Python. У них те же функции, что и у **обычных кортежей**, но их значения можно получать как с помощью имени (через точку, например .name), так и с помощью индекса (например [0]). Именованные кортежи улучшают читаемость кода и отдельных функций. Пример создания:
Topic = namedtuple('Topic', ['id', 'blog_id'])
Topic(id=1, blog_id=1)
Как и стандартные кортежи в Python, именованные кортежи являются неизменяемыми. Это значит, что после объявления значения кортежа не могут быть изменены. При изменении будет вызвано исключение AttributeError.
t = Topic(id=1, blog_id=1)
t.id = 2
---> 5 t.id = 2
AttributeError: can't set attribute
defaultdict
defaultdict ничем не отличается от обычного словаря, за исключением того, что если происходит обращение к словарю по ключу, которого нет в словаре, то будет вызвана функция, возвращающая значение по умолчанию. Поведение обычного словаря:
ordinary_dict = {}
ordinary_dict["key"]
---> ordinary_dict["key"]
KeyError: 'key'
Поведения defaultdict:
dd_dict = defaultdict(lambda: 5)
dd_dict["key"]
>>> 5
Counter
Это вид словаря, который предназначен для удобных и быстрых подсчетов количества появлений элементов в последовательностях.
from collections import Counter
# пустой счетчик
cnt = Counter()
# счетчик из последовательности
cnt = Counter('python')
# счетчик из словаря
cnt = Counter({'red': 0, 'green': 2})
# счетчик из ключевых слов 'args'
cnt = Counter(red=0, green=2)
Счетчик Counter имеет интерфейс словаря, за исключением того, что он возвращает 0 для отсутствующих элементов вместо вызова исключения KeyError:
cnt = Counter(['eggs', 'ham'])
cnt['bacon']
>>> 0
**Методы** Метод **Counter.elements()** возвращает итератор, в котором каждый элемент повторяется столько раз, во сколько установлено его значение.
cnt = Counter(a=4, b=2, c=0, d=-2)
list(cnt.elements())
>>> ['a', 'a', 'a', 'a', 'b', 'b']
Метод Counter.most_common() возвращает список из n наиболее распространенных элементов и их количество.
cnt = Counter("https://docs.python.org/3/library/itertools.html")
list(cnt.most_common(3))
>>> [('t', 6), ('/', 5), ('o', 5)]
Counter поддерживает математические операции. Сложение и вычитание объединяют счетчики путем сложения или вычитания количества соответствующих элементов. Пересечение и объединение возвращают минимум и максимум соответствующих отсчетов.
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
# Сложение счетчиков
c + d
# Counter({'a': 4, 'b': 3})
# Вычитание счетчиков, сохраняются только положительные значения элементов
c - d
# Counter({'a': 2})
# Пересечение счетчиков
c & d
# Counter({'a': 1, 'b': 1})
# Объединение счетчиков: max(c[x], d[x])
c | d
# Counter({'a': 3, 'b': 2})
Оглавление
Генераторы и итераторы
Итератор — любой объект, в котором есть два метода: __iter__ и __next__.
class Counter:
def __init__(self):
self.n = 0
def __iter__(self):
return self
def __next__(self):
y = self.n
self.n += 1
return y
Стоит избегать прямого вызова __iter__ и __next__. При использовании for или list comprehension Python вызывает эти методы сам. Если все-таки необходимо вызвать методы напрямую, используйте встроенные функции iter и next. В параметры нужно передать итера
c = Counter()
it = iter(c)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
Стандартные итераторы
Списки
a = [1, 2, 3]
print(iter(a))
for item in a:
print('arr item', item)
Кортежи
b = (1, 2, 3)
print(iter(b))
for item in b:
print('tuple item', item)
Словари
Все вышеперечисленные объекты являются итераторами, они реализуют методы __iter__ и __next__
c = {'var1': 1}
print(iter(c))
for item in c:
print(item)
Генераторы
Генераторы — функции, которые внутри используют выражение yield. Каждый вызов метода __next__ у объекта-генератора возвращает следующее значение. Метод __iter__ также реализуется автоматически. То есть генераторы можно использовать везде, где требуются и
def counter():
n = 0
while True:
yield n
n += 1
c = counter()
it = iter(c)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
List comprehensions
Синтаксический «сахар», который позволяет свернуть стандартные конструкции языка (циклы и условия) до одной строки.
Списки
lst = [item for item in arr]
Генераторы
gen = (item for item in arr)
Словари
Во всех типах list comprehensions можно использовать условия. Например:
dct = {item: item**2 for item in arr}
lst = [item for item in arr if item % 2 == 0]
В результирующем списке lst будут только элементы, которые делятся на 2.
Тернарные операции
Синтаксис, который эквивалентен обычному условию if, но при этом его можно использовать внутри одной строки. То есть, например, использовать внутри вызова функции, что позволяет сделать код более компактным.
res = 'четное' if a % 2 else 'нечетное'
эквивалентно
res = 'четное'
if a % 2:
res = 'четное'
else:
res = 'нечетное'