8. Списки, словари и кортежи

Продвинутая работа со списками
Заголовок раздела «Продвинутая работа со списками»# Сортировкаnums = [3, 1, 4, 1, 5, 9, 2, 6]nums.sort() # изменяет на месте: [1, 1, 2, 3, 4, 5, 6, 9]sorted(nums) # возвращает новый список
# Сортировка объектовusers = [ {"name": "Вера", "age": 30, "salary": 80000}, {"name": "Анна", "age": 25, "salary": 100000}, {"name": "Гена", "age": 22, "salary": 70000},]
by_age = sorted(users, key=lambda u: u["age"])by_salary_desc = sorted(users, key=lambda u: u["salary"], reverse=True)
# Сортировка по нескольким полямby_age_then_name = sorted(users, key=lambda u: (u["age"], u["name"]))
# Срезы (slices)nums = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums[2:5] # [2, 3, 4]nums[:3] # [0, 1, 2]nums[7:] # [7, 8, 9]nums[::2] # [0, 2, 4, 6, 8] — каждый второйnums[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] — реверс
# Изменение срезаnums[2:5] = [20, 30, 40] # замена элементовnums[::2] = [0]*5 # каждый второй = 0
# Копированиеoriginal = [1, [2, 3], 4]shallow = original.copy() # мелкая копияshallow = original[:] # то же самое
import copydeep = copy.deepcopy(original) # глубокая копияСтек и очередь
Заголовок раздела «Стек и очередь»# Список как стек (LIFO)stack = []stack.append(1) # pushstack.append(2)stack.append(3)stack.pop() # pop -> 3
# Список как очередь (FIFO) — но медленно!# Лучше использовать dequefrom collections import deque
queue = deque()queue.append("первый") # добавить в конецqueue.append("второй")queue.popleft() # удалить из начала -> "первый"collections — супер-инструменты
Заголовок раздела «collections — супер-инструменты»from collections import Counter, defaultdict, OrderedDict, namedtuple
# Counter — подсчёт элементовtext = "banana"counts = Counter(text)# Counter({'a': 3, 'n': 2, 'b': 1})
words = "the cat sat on the mat".split()word_counts = Counter(words)word_counts.most_common(3) # [('the', 2), ('cat', 1), ('sat', 1)]
# defaultdict — словарь с дефолтным значениемgraph = defaultdict(list)graph["A"].append("B") # не нужно проверять наличие ключаgraph["A"].append("C")graph["B"].append("D")
# Группировка по полюstudents = [("Анна", "Python"), ("Борис", "Java"), ("Вера", "Python")]by_language = defaultdict(list)for name, lang in students: by_language[lang].append(name)# {"Python": ["Анна", "Вера"], "Java": ["Борис"]}
# namedtuple — как dataclass, но прощеPoint = namedtuple("Point", ["x", "y"])p = Point(10, 20)print(p.x, p.y) # 10 20print(p[0], p[1]) # 10 20 (как кортеж)Продвинутая работа со словарями
Заголовок раздела «Продвинутая работа со словарями»# setdefault — безопасная установкаd = {}d.setdefault("key", []).append("value") # создаёт ключ если нет
# update — слияние словарейconfig = {"debug": False, "port": 8000}overrides = {"debug": True, "host": "localhost"}config.update(overrides)# {"debug": True, "port": 8000, "host": "localhost"}
# Python 3.9+: оператор |merged = config | overrides
# Удаление с получением значенияvalue = config.pop("debug", None) # None если ключа нет
# Сортировка словаряd = {"banana": 3, "apple": 5, "cherry": 1}sorted_by_value = dict(sorted(d.items(), key=lambda x: x[1]))# {"cherry": 1, "banana": 3, "apple": 5}
# Инвертирование словаряoriginal = {"a": 1, "b": 2, "c": 3}inverted = {v: k for k, v in original.items()}# {1: "a", 2: "b", 3: "c"}
# Глубокое обновлениеdef deep_update(base: dict, updates: dict) -> dict: """Рекурсивное обновление словаря.""" for key, value in updates.items(): if isinstance(value, dict) and key in base: deep_update(base[key], value) else: base[key] = value return baseDataclasses — современный способ хранения данных
Заголовок раздела «Dataclasses — современный способ хранения данных»from dataclasses import dataclass, fieldfrom datetime import datetime
@dataclassclass User: name: str email: str age: int role: str = "user" # с дефолтным значением tags: list[str] = field(default_factory=list) # изменяемый дефолт created_at: datetime = field(default_factory=datetime.now)
def is_admin(self) -> bool: return self.role == "admin"
print(user.name) # "Яша"print(user.is_admin()) # False
# dataclass автоматически создаёт __init__, __repr__, __eq__print(user)# User(name='Яша', email='[email protected]', age=2, role='user', ...)
# Frozen dataclass — неизменяемый@dataclass(frozen=True)class Point: x: float y: float
def distance(self, other: "Point") -> float: return ((self.x - other.x)**2 + (self.y - other.y)**2) ** 0.5
p1 = Point(0, 0)p2 = Point(3, 4)p1.distance(p2) # 5.0# p1.x = 10 — ОШИБКА! frozenTypedDict — типизированные словари
Заголовок раздела «TypedDict — типизированные словари»from typing import TypedDict, Required, NotRequired
class UserDict(TypedDict): name: str email: str age: int role: NotRequired[str] # опциональное поле
# IDE покажет ошибку если структура невернаПрактический пример: обработка данных
Заголовок раздела «Практический пример: обработка данных»from collections import defaultdict, Counterfrom dataclasses import dataclass, field
@dataclassclass Order: id: int user_id: int product: str amount: float status: str
orders = [ Order(1, 101, "Python Book", 29.99, "completed"), Order(2, 102, "JS Book", 24.99, "completed"), Order(3, 101, "React Course", 49.99, "pending"), Order(4, 103, "Python Book", 29.99, "completed"), Order(5, 102, "Python Book", 29.99, "cancelled"),]
# Статистика по продуктамcompleted = [o for o in orders if o.status == "completed"]by_product = Counter(o.product for o in completed)# Counter({"Python Book": 2, "JS Book": 1})
# Выручка по пользователямrevenue = defaultdict(float)for order in completed: revenue[order.user_id] += order.amount
top_users = sorted(revenue.items(), key=lambda x: x[1], reverse=True)Задание
Заголовок раздела «Задание»# Задание 1: Инвентарь магазинаinventory = [ {"name": "Молоко", "price": 89.5, "category": "dairy", "qty": 50}, {"name": "Хлеб", "price": 45.0, "category": "bakery", "qty": 30}, {"name": "Сыр", "price": 350.0, "category": "dairy", "qty": 15}, {"name": "Кефир", "price": 65.0, "category": "dairy", "qty": 40}, {"name": "Булка", "price": 35.0, "category": "bakery", "qty": 60},]# Реализуй:# 1. Общая стоимость инвентаря (price * qty)# 2. Средняя цена по категориям# 3. Топ-3 товара по стоимости запаса# 4. Функция search(query) — поиск по названию (без учёта регистра)
# Задание 2: Dataclass# Создай dataclass Product с полями: name, price, category, in_stock# Добавь метод discount(percent) -> "Product" — возвращает новый продукт со скидкойВ следующем уроке — ООП в Python!