Какие знаешь элементы функционального программирования в Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Функциональное программирование в Python
Практически функциональное программирование — парадигма, которая воспринимает вычисления как вычисление математических функций, избегая изменяемого состояния. Python поддерживает множество FP элементов благодаря гибкости языка.
Чистые функции (Pure Functions)
Функции, которые:
- Для одного входа всегда возвращают одинаковый выход
- Не имеют побочных эффектов (не изменяют внешнее состояние)
# Плохо - не чистая функция
counter = 0
def increment():
global counter
counter += 1
return counter
print(increment()) # 1
print(increment()) # 2
print(increment()) # 3
# Хорошо - чистая функция
def pure_increment(n):
return n + 1
print(pure_increment(0)) # 1
print(pure_increment(0)) # 1
print(pure_increment(0)) # 1
Преимущества чистых функций:
- Легче тестировать
- Кэшируемы (memoization)
- Параллельно безопасны
- Проще рассуждать о коде
First-Class Functions (функции как объекты)
Функции можно передавать как аргументы и возвращать из других функций:
# Функция принимает функцию как аргумент
def apply_operation(x, y, operation):
return operation(x, y)
def add(a, b):
return a + b
def multiply(a, b):
return a * b
print(apply_operation(5, 3, add)) # 8
print(apply_operation(5, 3, multiply)) # 15
# Функция возвращает функцию (высший порядок)
def create_multiplier(n):
def multiplier(x):
return x * n
return multiplier
multiply_by_3 = create_multiplier(3)
print(multiply_by_3(10)) # 30
Higher-Order Functions (функции высшего порядка)
map()
Применяет функцию к каждому элементу итерируемого объекта:
numbers = [1, 2, 3, 4, 5]
# Императивный подход
result = []
for num in numbers:
result.append(num ** 2)
# Функциональный подход
result = list(map(lambda x: x ** 2, numbers))
print(result) # [1, 4, 9, 16, 25]
# Или с обычной функцией
def square(x):
return x ** 2
result = list(map(square, numbers))
filter()
Отбирает элементы, для которых функция возвращает True:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Императивный
even = []
for num in numbers:
if num % 2 == 0:
even.append(num)
# Функциональный
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # [2, 4, 6, 8, 10]
reduce()
Аккумулирует значения в один результат:
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# Вычисление произведения
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# С начальным значением
total = reduce(lambda x, y: x + y, numbers, 0)
print(total) # 15
# Практический пример
orders = [100, 200, 150, 300]
total_amount = reduce(lambda sum, order: sum + order, orders, 0)
print(f'Total: ${total_amount}') # Total: $750
Lambda функции
Анонимные функции для простых операций:
# Простые примеры
square = lambda x: x ** 2
print(square(5)) # 25
# С несколькими аргументами
add = lambda x, y: x + y
print(add(3, 4)) # 7
# Используется с map/filter
nums = [1, 2, 3, 4, 5]
filtered = list(filter(lambda x: x > 2, nums))
print(filtered) # [3, 4, 5]
# Сортировка по сложному ключу
students = [
{'name': 'Alice', 'grade': 85},
{'name': 'Bob', 'grade': 92},
{'name': 'Charlie', 'grade': 78}
]
sorted_students = sorted(students, key=lambda s: s['grade'], reverse=True)
Closures (замыкания)
Вложенная функция имеет доступ к переменным внешней функции:
def make_adder(x):
def adder(y):
return x + y # Замыкание над 'x'
return adder
add_5 = make_adder(5)
print(add_5(3)) # 8
print(add_5(10)) # 15
# Практический пример - логирование
def create_logger(prefix):
def log(message):
print(f"[{prefix}] {message}")
return log
error_log = create_logger("ERROR")
info_log = create_logger("INFO")
error_log("Something went wrong")
info_log("Process started")
Decorators
Функции, которые оборачивают другие функции для расширения их поведения:
# Простой декоратор
def timer(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} took {elapsed:.4f} seconds")
return result
return wrapper
@timer
def slow_function(n):
import time
time.sleep(n)
return f"Done after {n} seconds"
slow_function(1)
# Декоратор с параметрами
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Immutability (неизменяемость)
Использование неизменяемых структур данных:
# Плохо - мутируемое состояние
config = {'timeout': 30, 'retries': 3}
config['timeout'] = 60 # Изменяем
# Хорошо - неизменяемое состояние
from collections import namedtuple
Config = namedtuple('Config', ['timeout', 'retries'])
config1 = Config(timeout=30, retries=3)
config2 = config1._replace(timeout=60) # Создаём новый объект
print(config1) # Config(timeout=30, retries=3)
print(config2) # Config(timeout=60, retries=3)
# Использование tuples вместо lists
point = (1, 2, 3) # Неизменяемо
# list_point = [1, 2, 3] # Мутируемо
Composition (композиция функций)
Объединение простых функций в сложные:
# Функции-кирпичики
def add_tax(price):
return price * 1.2
def apply_discount(price, discount):
return price * (1 - discount)
def format_price(price):
return f"${price:.2f}"
# Композиция
def compose(*functions):
def composed(arg):
result = arg
for func in reversed(functions):
result = func(result)
return result
return composed
# Создание конвейера
final_price = compose(
format_price,
add_tax,
lambda p: apply_discount(p, 0.1) # 10% скидка
)
original_price = 100
print(final_price(original_price)) # $129.60
Практический пример: Обработка данных
from functools import reduce
# Данные
orders = [
{'id': 1, 'amount': 100, 'status': 'completed'},
{'id': 2, 'amount': 200, 'status': 'pending'},
{'id': 3, 'amount': 150, 'status': 'completed'},
{'id': 4, 'amount': 300, 'status': 'completed'},
]
# FP подход
total_revenue = reduce(
lambda sum, order: sum + order['amount'],
filter(lambda o: o['status'] == 'completed', orders),
0
)
print(f"Total revenue: ${total_revenue}") # $550
# Альтернатива с list comprehension (более Pythonic)
total_revenue_v2 = sum(o['amount'] for o in orders if o['status'] == 'completed')
print(f"Total revenue: ${total_revenue_v2}") # $550
Когда использовать FP в Python
Используй функциональный стиль когда:
- Нужна параллельная обработка данных
- Код должен быть предсказуемым и тестируемым
- Работаешь с трансформациями данных (ETL)
- Нужна композиция функций
Python лучше всего с гибридным подходом:
# Не переусложняй - Python не 100% FP язык
# Смешивай OOP и FP когда это имеет смысл
class DataProcessor:
def __init__(self, data):
self.data = data
def filter_completed(self):
return list(filter(lambda x: x['status'] == 'completed', self.data))
def sum_amounts(self):
return reduce(lambda s, x: s + x['amount'], self.data, 0)
Питон поддерживает функциональное программирование как один из инструментов, но язык оптимален с многопарадигмальным подходом, комбинирующим OOP, функциональное и процедурное стили.