Многоуровневая сортировка в Python: техники для сложных данных

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики, изучающие Python и его возможности работы со структурами данных
  • Студенты и начинающие программисты, стремящиеся улучшить свои навыки в сортиовании данных
  • Эксперты и практикующие программисты, заинтересованные в оптимизации кода и повышении производительности при работе с большими объемами данных

    Структуры данных в Python регулярно требуют не просто "расставить элементы по порядку", а создать сложную иерархию приоритетов. Когда ваш код сортирует сотрудников сначала по отделу, затем по стажу, а потом по фамилии – вы погружаетесь в мир многоуровневой сортировки. Эффективное владение этими методами отличает рядового кодера от Python-мастера. Случайная сортировка способна превратить рабочий код в источник неуловимых багов, а точное управление сортировкой – критически важный навык для работы с данными любой сложности 🧩.

Хотите превратить сырые знания о сортировке в Python в профессиональный инструментарий разработчика? На курсе Обучение Python-разработке от Skypro вы не только освоите многоуровневую сортировку данных, но и научитесь применять эти техники в реальных проектах под руководством практикующих экспертов. От базовых алгоритмов до продвинутой оптимизации кода – погружение в Python-разработку с нуля до уровня, востребованного на рынке.

Основы многоуровневой сортировки в Python: принципы работы

Многоуровневая сортировка в Python – это техника упорядочивания элементов коллекции по нескольким критериям с установленным приоритетом. Когда значения первичного ключа совпадают, в игру вступает вторичный критерий, затем третичный и так далее. Это фундаментальный паттерн работы с данными, который используется повсеместно – от сортировки таблиц до построения сложных отчётов.

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

Алексей Демидов, ведущий разработчик Python

Однажды нам поступила задача отображать в интерфейсе список заказов, отсортированный одновременно по трём параметрам: статусу (сначала активные, потом завершённые), приоритету (от высокого к низкому) и времени создания (от новых к старым). Мы попытались решить задачу последовательной сортировкой: сначала по времени, потом по приоритету, в конце по статусу. Результат был неправильным – последовательность сортировки имеет значение! Правильный порядок обратный: сначала по наименее значимому критерию (времени), затем по более важному (приоритету), и наконец по самому важному (статусу). Стабильность алгоритма сортировки гарантирует, что предыдущий порядок не нарушится при следующей сортировке, если значения равны.

Механизм сравнения в Python опирается на несколько ключевых принципов:

  • При сортировке элементов Python применяет операторы сравнения (<, >, ==)
  • Встроенные типы имеют предопределённые правила сравнения
  • Пользовательские объекты могут определять своё поведение при сравнении через магические методы (lt, gt и др.)
  • Функция ключа (key) преобразует элементы в сопоставимые значения

Рассмотрим простейший пример многоуровневой сортировки с использованием кортежей:

Python
Скопировать код
# Список студентов: (имя, курс, средний балл)
students = [
("Алексей", 2, 4.5),
("Мария", 1, 4.8),
("Иван", 2, 4.2),
("Елена", 1, 4.8)
]

# Сортировка по курсу (по возрастанию), затем по среднему баллу (по убыванию)
sorted_students = sorted(students, key=lambda x: (x[1], -x[2]))
print(sorted_students)
# Результат: [('Елена', 1, 4.8), ('Мария', 1, 4.8), ('Иван', 2, 4.2), ('Алексей', 2, 4.5)]

Принципиальное отличие многоуровневой сортировки от последовательных вызовов состоит в эффективности: один проход через данные вместо нескольких. А это критически важно при работе с большими объёмами информации.

Характеристика Одиночная сортировка Последовательные сортировки Многоуровневая сортировка
Временная сложность O(n log n) O(k * n log n) где k – число критериев O(n log n)
Количество проходов по данным 1 k (число критериев) 1
Простота реализации Высокая Средняя Средняя
Гибкость направления сортировки Низкая Высокая Высокая
Пошаговый план для смены профессии

Сортировка с использованием lambda функций: составные ключи

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

Основная идея использования lambda с составными ключами – возвращать кортеж значений, по которым будет проводиться сравнение:

Python
Скопировать код
# Каталог товаров: название, категория, цена, рейтинг
products = [
{"name": "Ноутбук", "category": "Электроника", "price": 45000, "rating": 4.8},
{"name": "Смартфон", "category": "Электроника", "price": 20000, "rating": 4.5},
{"name": "Кофемашина", "category": "Бытовая техника", "price": 15000, "rating": 4.7},
{"name": "Наушники", "category": "Электроника", "price": 5000, "rating": 4.9},
]

# Сортировка по категории, затем по убыванию рейтинга, затем по возрастанию цены
sorted_products = sorted(products, key=lambda x: (
x["category"], # первичный ключ – категория (лексикографически)
-x["rating"], # вторичный ключ – рейтинг (по убыванию)
x["price"] # третичный ключ – цена (по возрастанию)
))

for p in sorted_products:
print(f"{p['name']} – {p['category']}, рейтинг: {p['rating']}, цена: {p['price']}")

Обратите внимание на минус перед x["rating"] – это типичный приём для сортировки по убыванию в составном ключе. Однако этот подход работает только с числовыми значениями. Для строк и других типов потребуется другое решение.

Когда требуется комбинировать разные направления сортировки для разных типов данных, можно использовать более сложный подход:

Python
Скопировать код
from functools import cmp_to_key

# Сортировка с разными направлениями для разных типов данных
def custom_compare(a, b):
# Сначала по категории (по возрастанию)
if a["category"] != b["category"]:
return -1 if a["category"] < b["category"] else 1

# Затем по рейтингу (по убыванию)
if a["rating"] != b["rating"]:
return -1 if a["rating"] > b["rating"] else 1

# И наконец по цене (по возрастанию)
return -1 if a["price"] < b["price"] else 1

# Применение пользовательской функции сравнения
sorted_products = sorted(products, key=cmp_to_key(custom_compare))

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

Часто требуется сортировать по вычисляемым значениям или применять функции преобразования. Lambda-функции справляются и с этим:

Python
Скопировать код
# Сортировка по длине имени, затем по алфавиту
names = ["Александр", "Иван", "Мария", "Анна", "Екатерина"]
sorted_names = sorted(names, key=lambda x: (len(x), x))
print(sorted_names)
# Результат: ['Анна', 'Иван', 'Мария', 'Александр', 'Екатерина']

# Сортировка словарей с игнорированием регистра
words = [{"word": "Apple"}, {"word": "banana"}, {"word": "Cherry"}, {"word": "date"}]
sorted_words = sorted(words, key=lambda x: x["word"].lower())
print([w["word"] for w in sorted_words])
# Результат: ['Apple', 'banana', 'Cherry', 'date']

Еще одно преимущество lambda-функций – возможность использовать условную логику прямо в ключе сортировки:

Python
Скопировать код
# Данные сотрудников
employees = [
{"name": "Алексей", "department": "Разработка", "is_manager": False, "salary": 120000},
{"name": "Ирина", "department": "Маркетинг", "is_manager": True, "salary": 150000},
{"name": "Сергей", "department": "Разработка", "is_manager": True, "salary": 180000},
{"name": "Ольга", "department": "Маркетинг", "is_manager": False, "salary": 90000},
]

# Сортировка: сначала руководители, затем по отделам, потом по зарплате (по убыванию)
sorted_employees = sorted(employees, key=lambda x: (
0 if x["is_manager"] else 1, # Сначала руководители
x["department"], # Затем по отделу
-x["salary"] # Потом по зарплате (по убыванию)
))

for e in sorted_employees:
print(f"{e['name']} – {e['department']}, " + 
f"{'Руководитель' if e['is_manager'] else 'Сотрудник'}, " +
f"зарплата: {e['salary']}")

Приём сортировки Пример использования Плюсы Минусы
Составной кортеж lambda x: (x[0], x[1]) Компактность, производительность Ограниченная гибкость с направлением
Инверсия числового значения lambda x: (x[0], -x[1]) Простая реализация убывания Работает только с числами
Условный ключ lambda x: (0 if x.is_active else 1, x.name) Гибкость в приоритизации Может усложнить чтение кода
Функция cmp_to_key cmp_to_key(custom_compare) Максимальная гибкость Снижение производительности, многословность

Метод sorted() и sort() для многокритериальной сортировки

Python предоставляет два основных метода для сортировки: функцию sorted() и метод списков .sort(). Оба поддерживают многокритериальную сортировку, но имеют существенные различия, о которых нужно помнить при выборе подходящего инструмента.

Ключевые различия между sorted() и sort():

  • sorted() – функция, создающая новый отсортированный список, не изменяя исходный
  • list.sort() – метод, который сортирует список на месте, изменяя исходный объект
  • sorted() может принимать любой итерируемый объект, включая кортежи, словари, строки
  • list.sort() доступен только для списков
  • sorted() возвращает новый список, что позволяет использовать его в выражениях
  • list.sort() возвращает None, что предотвращает цепочку вызовов

Рассмотрим практические примеры многокритериальной сортировки с использованием обоих методов:

Python
Скопировать код
# Список студентов с разными атрибутами
students = [
{"name": "Алексей", "group": "А", "grade": 85, "attendance": 90},
{"name": "Мария", "group": "Б", "grade": 92, "attendance": 95},
{"name": "Дмитрий", "group": "А", "grade": 78, "attendance": 85},
{"name": "Светлана", "group": "Б", "grade": 92, "attendance": 88},
{"name": "Иван", "group": "А", "grade": 85, "attendance": 92},
]

# Использование sorted() – сортировка по группе, затем по оценке (по убыванию)
sorted_students = sorted(
students, 
key=lambda s: (s["group"], -s["grade"], -s["attendance"])
)

print("Результат sorted():")
for s in sorted_students:
print(f"{s['name']}: группа {s['group']}, оценка {s['grade']}, посещаемость {s['attendance']}%")

# Использование sort() – модифицирует исходный список
students_copy = students.copy() # создаём копию, чтобы не изменить оригинал
students_copy.sort(key=lambda s: (s["group"], -s["grade"], -s["attendance"]))

print("\nРезультат sort():")
for s in students_copy:
print(f"{s['name']}: группа {s['group']}, оценка {s['grade']}, посещаемость {s['attendance']}%")

При работе с обоими методами можно использовать дополнительный параметр reverse=True для общего обращения порядка сортировки. Однако это влияет на все критерии сортировки одновременно, что не всегда желательно при многоуровневой сортировке.

Максим Ковалёв, архитектор данных

При работе с финансовой платформой мы столкнулись с интересным кейсом. Нам нужно было отображать транзакции пользователей, сгруппированные по типу операции, внутри каждой группы – отсортированные по дате (новые сверху), а при одинаковых датах – по сумме (от большей к меньшей). В первой итерации кода мы использовали три последовательных вызова sort(), для каждого критерия отдельно. Система работала, но на больших объёмах данных скорость была неприемлемой.

После рефакторинга мы перешли на многокритериальную сортировку с составным ключом:

Python
Скопировать код
transactions.sort(key=lambda t: (
t['operation_type'],
-int(t['date'].timestamp()), # Отрицательный timestamp для сортировки по убыванию
-t['amount']
))

Производительность выросла в разы, особенно заметно на выборках по 10000+ транзакций. Составные ключи сортировки – настоящая находка для подобных задач!

При работе со сложными объектами часто требуется извлечение или преобразование атрибутов. Функциональный стиль Python позволяет делать это элегантно:

Python
Скопировать код
# Класс для представления книги
class Book:
def __init__(self, title, author, year, rating):
self.title = title
self.author = author
self.year = year
self.rating = rating

def __repr__(self):
return f"{self.title} ({self.year}) by {self.author}, рейтинг: {self.rating}"

# Создание списка книг
books = [
Book("Война и мир", "Толстой Л.Н.", 1869, 4.9),
Book("Преступление и наказание", "Достоевский Ф.М.", 1866, 4.8),
Book("Мастер и Маргарита", "Булгаков М.А.", 1967, 4.9),
Book("Анна Каренина", "Толстой Л.Н.", 1877, 4.7),
]

# Сортировка по автору, году публикации и рейтингу (по убыванию)
sorted_books = sorted(books, key=lambda book: (book.author, book.year, -book.rating))

print("Отсортированный список книг:")
for book in sorted_books:
print(book)

Для более сложных сценариев можно комбинировать методы сортировки с группировкой и другими операциями над данными:

Python
Скопировать код
# Группировка и сортировка внутри групп
from itertools import groupby

# Сначала отсортируем по автору для правильной группировки
books.sort(key=lambda book: book.author)

# Группировка по автору и сортировка внутри групп по году (по возрастанию)
for author, books_by_author in groupby(books, key=lambda book: book.author):
print(f"\nКниги автора {author}:")

# Преобразуем итератор в список и отсортируем по году
author_books = sorted(list(books_by_author), key=lambda book: book.year)

for book in author_books:
print(f" {book.title} ({book.year}), рейтинг: {book.rating}")

Применение itemgetter и attrgetter для эффективной сортировки

Использование lambda-функций для сортировки интуитивно понятно, но для повышения производительности Python предлагает специализированные инструменты из модуля operator: itemgetter и attrgetter. Эти функции оптимизированы для извлечения значений из объектов и работают быстрее, чем эквивалентные lambda-выражения.

itemgetter идеально подходит для сортировки по ключам словарей или индексам последовательностей:

Python
Скопировать код
from operator import itemgetter

# Список словарей с информацией о продуктах
products = [
{"id": 1, "name": "Ноутбук", "price": 45000, "stock": 12},
{"id": 2, "name": "Смартфон", "price": 20000, "stock": 45},
{"id": 3, "name": "Планшет", "price": 30000, "stock": 8},
{"id": 4, "name": "Монитор", "price": 15000, "stock": 24},
]

# Сортировка с использованием itemgetter (по наличию, затем по цене)
sorted_by_stock_price = sorted(
products, 
key=itemgetter("stock", "price")
)

print("Сортировка по запасам и цене:")
for product in sorted_by_stock_price:
print(f"{product['name']}: {product['stock']} шт., {product['price']} ₽")

# Сортировка по нескольким критериям с разным направлением
# (сначала по убыванию запасов, затем по возрастанию цены)
from functools import cmp_to_key

def custom_compare(a, b):
# По запасам (по убыванию)
if a["stock"] != b["stock"]:
return -1 if a["stock"] > b["stock"] else 1

# По цене (по возрастанию)
if a["price"] != b["price"]:
return -1 if a["price"] < b["price"] else 1

return 0

sorted_custom = sorted(products, key=cmp_to_key(custom_compare))

print("\nСложная сортировка (запасы по убыванию, цена по возрастанию):")
for product in sorted_custom:
print(f"{product['name']}: {product['stock']} шт., {product['price']} ₽")

Для работы с объектами классов оптимальным решением является использование attrgetter, который извлекает значения атрибутов объектов:

Python
Скопировать код
from operator import attrgetter

class Employee:
def __init__(self, name, department, salary, experience):
self.name = name
self.department = department
self.salary = salary
self.experience = experience

def __repr__(self):
return f"{self.name} ({self.department}): {self.salary}₽, опыт: {self.experience} лет"

# Список сотрудников
employees = [
Employee("Иванов А.С.", "Разработка", 120000, 5),
Employee("Петров И.И.", "Маркетинг", 90000, 3),
Employee("Сидорова О.П.", "Разработка", 150000, 7),
Employee("Кузнецова Е.В.", "Маркетинг", 100000, 5),
Employee("Смирнов Д.А.", "Разработка", 130000, 4),
]

# Сортировка по отделу и опыту
sorted_by_dept_exp = sorted(
employees, 
key=attrgetter("department", "experience")
)

print("Сортировка по отделу и опыту:")
for employee in sorted_by_dept_exp:
print(employee)

# Сортировка по нескольким критериям с разным направлением
# (отдел, затем по убыванию зарплаты)
from operator import attrgetter
import functools

def negate_numeric(obj, attr):
"""Возвращает отрицательное значение числового атрибута"""
return -getattr(obj, attr)

# Создаем составную функцию ключа
def composite_key(obj):
return (obj.department, negate_numeric(obj, "salary"))

sorted_employees = sorted(employees, key=composite_key)

print("\nСортировка по отделу и зарплате (по убыванию):")
for employee in sorted_employees:
print(employee)

Сравнение производительности различных методов сортировки на больших данных показывает преимущество специализированных функций:

Метод Время выполнения на 100,000 элементах Память Читаемость кода
Lambda 1.2x базовое время Базовое потребление Высокая
itemgetter 0.9x базовое время 0.95x базовое потребление Средняя
attrgetter 0.85x базовое время 0.95x базовое потребление Средняя
cmptokey 1.7x базовое время 1.1x базовое потребление Низкая

Преимущества использования itemgetter и attrgetter:

  • Улучшенная производительность по сравнению с lambda-функциями
  • Лаконичный синтаксис при извлечении нескольких полей
  • Чёткое выражение намерений в коде (ясно, что происходит извлечение атрибутов)
  • Возможность повторного использования созданных геттеров

Для комбинирования разных направлений сортировки с itemgetter или attrgetter можно использовать специализированные обертки:

Python
Скопировать код
from operator import itemgetter

# Вспомогательная функция для сортировки по убыванию
def desc_itemgetter(*items):
getter = itemgetter(*items)
return lambda obj: tuple(-x if isinstance(x, (int, float)) else x for x in getter(obj))

# Сортировка списка словарей по полю "stock" по убыванию, затем "price" по возрастанию
products = [
{"name": "Ноутбук", "price": 45000, "stock": 12},
{"name": "Смартфон", "price": 20000, "stock": 12},
{"name": "Планшет", "price": 30000, "stock": 8},
]

# Сначала сортируем по price (для случаев, когда stock одинаковый)
products.sort(key=itemgetter("price"))
# Затем сортируем по stock по убыванию
products.sort(key=lambda x: -x["stock"])

print("Результат сортировки:")
for p in products:
print(f"{p['name']}: {p['stock']} шт., {p['price']} ₽")

Сортировка объектов по нескольким атрибутам: практические кейсы

Многоуровневая сортировка – не просто теоретический концепт, а практический инструмент, решающий реальные проблемы разработки. Рассмотрим несколько распространенных сценариев, где этот подход демонстрирует свою ценность. 🔍

Наиболее частые задачи, требующие сортировки по нескольким атрибутам:

  • Отображение данных в пользовательском интерфейсе (таблицы, списки)
  • Формирование отчетов с группировкой данных
  • Поиск дубликатов и близких значений
  • Анализ трендов и отклонений
  • Организация данных для оптимизации доступа

Рассмотрим практический кейс: интернет-магазин, где товары нужно отображать по определенным критериям:

Python
Скопировать код
from dataclasses import dataclass
from datetime import datetime, timedelta
from operator import attrgetter
import random

@dataclass
class Product:
id: int
name: str
category: str
price: float
rating: float
stock: int
is_premium: bool
added_date: datetime

def __repr__(self):
return (
f"{self.name} ({self.category}): "
f"${self.price:.2f}, ⭐{self.rating:.1f}, "
f"{'🔶 Premium' if self.is_premium else ''},"
f" {self.stock} шт."
)

# Создание тестовых данных
categories = ["Электроника", "Одежда", "Книги", "Дом и сад"]
names = [
"Смартфон X10", "Ноутбук Pro", "Футболка классическая", 
"Джинсы универсальные", "Роман 'Война и мир'", "Учебник Python", 
"Садовый шланг", "Набор инструментов"
]

# Генерация случайных товаров
products = []
for i in range(20):
category = random.choice(categories)
name = random.choice(names)
premium = random.choice([True, False])

products.append(Product(
id=i+1,
name=f"{name} {i+1}",
category=category,
price=round(random.uniform(10, 1000), 2),
rating=round(random.uniform(3.0, 5.0), 1),
stock=random.randint(0, 50),
is_premium=premium,
added_date=datetime.now() – timedelta(days=random.randint(0, 365))
))

# Задача 1: Показать товары на главной странице
# – Сначала товары в наличии (stock > 0)
# – Затем премиум-товары выше обычных
# – В рамках премиальности, сортировка по рейтингу (по убыванию)
# – При равном рейтинге, сортировка по цене (по возрастанию)

def main_page_key(product):
return (
0 if product.stock > 0 else 1, # Товары в наличии выше
0 if product.is_premium else 1, # Премиум товары выше
-product.rating, # Высокий рейтинг выше
product.price # Низкая цена выше при равном рейтинге
)

main_page_products = sorted(products, key=main_page_key)

print("Товары для главной страницы:")
for i, product in enumerate(main_page_products[:10], 1): # Показываем топ-10
print(f"{i}. {product}")

# Задача 2: Товары для категориальной страницы
# – Сгруппированы по категории
# – Внутри категории по новизне (новые выше)
# – При равной дате добавления – по рейтингу (по убыванию)

def category_page_key(product):
return (
product.category,
-int(product.added_date.timestamp()), # Отрицательный timestamp для сортировки по убыванию
-product.rating
)

category_page_products = sorted(products, key=category_page_key)

print("\nТовары для категориальных страниц:")
current_category = None
for product in category_page_products:
if product.category != current_category:
current_category = product.category
print(f"\n--- {current_category} ---")

print(f"{product.name}: рейтинг {product.rating}, добавлен {product.added_date.strftime('%d.%m.%Y')}")

Усложним задачу: рассмотрим сортировку связанных объектов в сложной структуре данных:

Python
Скопировать код
from collections import defaultdict

@dataclass
class Order:
id: int
customer_id: int
date: datetime
total: float
status: str

def __repr__(self):
return f"Заказ #{self.id}: ${self.total:.2f}, {self.date.strftime('%d.%m.%Y')}, {self.status}"

# Создание тестовых заказов
statuses = ["Новый", "Обработка", "Отправлен", "Доставлен", "Отменен"]
orders = []
for i in range(50):
customer_id = random.randint(1, 10)
date = datetime.now() – timedelta(days=random.randint(0, 90))

orders.append(Order(
id=i+1,
customer_id=customer_id,
date=date,
total=round(random.uniform(10, 500), 2),
status=random.choice(statuses)
))

# Группировка заказов по клиентам
customer_orders = defaultdict(list)
for order in orders:
customer_orders[order.customer_id].append(order)

# Сортировка заказов для каждого клиента
# – Сначала по статусу (в порядке важности: Новый, Обработка, Отправлен, Доставлен, Отменен)
# – Затем по дате (новые выше)
# – При равной дате по сумме (большие выше)

status_priority = {
"Новый": 0,
"Обработка": 1,
"Отправлен": 2,
"Доставлен": 3,
"Отменен": 4
}

def order_sort_key(order):
return (
status_priority[order.status],
-int(order.date.timestamp()),
-order.total
)

# Сортировка заказов для каждого клиента
for customer_id, orders in customer_orders.items():
customer_orders[customer_id] = sorted(orders, key=order_sort_key)

# Показ отсортированных заказов
print("\nЗаказы по клиентам:")
for customer_id in sorted(customer_orders.keys())[:3]: # Показываем только первых 3 клиентов
print(f"\nКлиент #{customer_id}:")
for order in customer_orders[customer_id]:
print(f" {order}")

Одна из интересных задач – эффективная сортировка разнородных объектов, где критерии могут быть вычисляемыми или зависеть от сложной бизнес-логики:

Python
Скопировать код
@dataclass
class Content:
id: int
title: str
type: str # "article", "video", "podcast"
publish_date: datetime
views: int
likes: int
duration: int = 0 # в секундах для видео/подкастов

@property
def engagement_rate(self):
"""Вычисляемый показатель вовлеченности"""
if self.views == 0:
return 0
return (self.likes / self.views) * 100

def __repr__(self):
type_emoji = {"article": "📄", "video": "🎬", "podcast": "🎙️"}
return (
f"{type_emoji.get(self.type, '❓')} {self.title} – "
f"👁️ {self.views}, ❤️ {self.likes}, "
f"ER: {self.engagement_rate:.1f}%"
)

# Создаем тестовый контент
content_items = []
for i in range(30):
content_type = random.choice(["article", "video", "podcast"])
views = random.randint(100, 10000)
likes = random.randint(0, min(views, 2000))
duration = 0 if content_type == "article" else random.randint(60, 3600)

content_items.append(Content(
id=i+1,
title=f"Контент #{i+1}",
type=content_type,
publish_date=datetime.now() – timedelta(days=random.randint(0, 30)),
views=views,
likes=likes,
duration=duration
))

# Задача: формирование персонализированной ленты контента
# Критерии персонализации:
# 1. Популярность (ER) – высокий ER выше
# 2. Свежесть – новые выше
# 3. Приоритет типов: видео > статьи > подкасты
# 4. Для видео – более короткие выше

type_priority = {"video": 0, "article": 1, "podcast": 2}

def personalized_feed_key(item):
# Нормализуем ER для сравнения (инверсия для сортировки по убыванию)
normalized_er = -item.engagement_rate

# Нормализуем дату (инверсия для сортировки по убыванию)
date_factor = -int(item.publish_date.timestamp())

# Фактор типа контента
type_factor = type_priority[item.type]

# Фактор длительности (только для видео и подкастов)
duration_factor = item.duration if item.type != "article" else 0

return (normalized_er, date_factor, type_factor, duration_factor)

# Сортировка контента для персонализированной ленты
personalized_feed = sorted(content_items, key=personalized_feed_key)

print("\nПерсонализированная лента контента:")
for i, item in enumerate(personalized_feed[:10], 1):
print(f"{i}. {item}")

Многоуровневая сортировка в Python — мощный инструмент, позволяющий элегантно решать сложные задачи упорядочивания данных. От простой сортировки списков до создания персонализированных лент контента — эти техники применимы практически в любом проекте. Помните, что выбор между lambda, itemgetter и attrgetter стоит делать осознанно, исходя из конкретных требований к производительности и читаемости кода. Составные ключи, манипуляции с направлением сортировки и группировка данных — это тот базовый арсенал, который превращает обычного кодера в мастера обработки данных.

Загрузка...