Перейти к основному содержимому

Функциональное программирование

📺 Слайды к лекции

Материалы в разработке

Эта страница находится в процессе подготовки и не является финальной версией. Содержание будет дорабатываться и обновляться в течение текущего семестра.

Функции как объекты первого класса

В Python функции — это объекты. Их можно присваивать переменным, передавать как аргументы и возвращать из других функций.

first_class.py
def greet(name):
return f"Hello, {name}"

say_hello = greet
print(say_hello("World")) # Hello, World

def apply(func, value):
return func(value)

print(apply(greet, "Python")) # Hello, Python
Определение

Функция первого класса (first-class function) — функция, которая может быть передана как аргумент, возвращена из другой функции и присвоена переменной. В Python все функции являются объектами первого класса.

Функции высшего порядка

Функция высшего порядка принимает другие функции как аргументы или возвращает их.

map, filter, reduce

higher_order.py
numbers = [1, 2, 3, 4, 5]

squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16, 25]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]

from functools import reduce
total = reduce(lambda a, b: a + b, numbers) # 15

Lambda-выражения

Lambda — анонимная функция в одну строку:

lambda.py
add = lambda a, b: a + b

students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
students.sort(key=lambda s: s[1], reverse=True)
Важно

Lambda-выражения ограничены одним выражением. Для сложной логики используйте обычные функции.

Замыкания (closures)

Замыкание — функция, которая захватывает переменные из окружающей области видимости:

closure.py
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Частая ошибка

Late binding в замыканиях:

funcs = [lambda x: x + i for i in range(3)]
print(funcs[0](0)) # 2 (а не 0!)

# Исправление:
funcs = [lambda x, i=i: x + i for i in range(3)]

Декораторы

Декоратор оборачивает функцию, добавляя ей поведение:

decorator.py
import functools

def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
import time
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
return result
return wrapper

@timer
def slow_function():
import time
time.sleep(1)

Декораторы с аргументами

decorator_args.py
def repeat(n):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator

@repeat(3)
def say_hello():
print("Hello!")

Модуль functools

partial — частичное применение

partial.py
from functools import partial

def power(base, exponent):
return base ** exponent

square = partial(power, exponent=2)
cube = partial(power, exponent=3)

lru_cache — мемоизация

lru_cache.py
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
Практический совет

lru_cache работает только с хэшируемыми аргументами. Для функций с dict/list аргументами конвертируйте их в tuple/frozenset.

singledispatch — перегрузка по типу

singledispatch.py
from functools import singledispatch

@singledispatch
def process(arg):
print(f"Default: {arg}")

@process.register(int)
def _(arg):
print(f"Integer: {arg * 2}")

@process.register(str)
def _(arg):
print(f"String: {arg.upper()}")

Модуль itertools

itertools_examples.py
import itertools

list(itertools.chain([1, 2], [3, 4])) # [1, 2, 3, 4]
list(itertools.islice(range(100), 5, 10)) # [5, 6, 7, 8, 9]
list(itertools.product('AB', [1, 2])) # декартово произведение
list(itertools.combinations('ABC', 2)) # сочетания

Генераторы и yield

Генератор использует yield и возвращает ленивый итератор:

generator.py
def fibonacci_gen():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

fib = fibonacci_gen()
for _ in range(10):
print(next(fib), end=" ") # 0 1 1 2 3 5 8 13 21 34
Определение

Генератор экономит память — он не хранит все значения сразу, а вычисляет каждое при вызове next().

Comprehensions

comprehensions.py
squares = [x**2 for x in range(10)]                    # list
word_lengths = {w: len(w) for w in ["hello", "world"]} # dict
unique_lengths = {len(w) for w in ["hi", "hello"]} # set
total = sum(x**2 for x in range(1000000)) # generator expression
Практический совет

Generator expression (x for x in ...) не создаёт список в памяти. Используйте для больших данных вместо list comprehension.