Все задачи практикума 6 сводятся к одной идее: список кортежей — это таблица, где каждый кортеж — строка. Примеры идут от базовой формы данных к полному конвейеру: обход → фильтр → форматирование → итоги → вложенные структуры. Весь вывод в комментариях получен запуском кода.
Пример 1. Список кортежей как мини-таблица
Запись вида ("Кофе", 127.99) — это одна строка данных: два поля хранятся вместе и всегда идут парой.
# 01_tuples_as_rows.py
products = [("Кофе", 127.99), ("Чай", 52.49), ("Шоколад", 81.99)]
for item in products:
print(item, item[0], item[1])
# ('Кофе', 127.99) Кофе 127.99
# ('Чай', 52.49) Чай 52.49
# ('Шоколад', 81.99) Шоколад 81.99
Что происходит: item — это кортеж целиком, а item[0]/item[1] — его поля по индексу. В следующих примерах вместо индексов используется распаковка — она читается понятнее.
Пример 2. Печать коллекции без цикла
Оператор * распаковывает список прямо в аргументы print(), а sep задаёт разделитель между ними.
# 02_print_without_loop.py
names = ["Молоко", "Хлеб", "Сахар"]
print(*names, sep="\n")
# Молоко
# Хлеб
# Сахар
print(*names, sep=", ")
# Молоко, Хлеб, Сахар
Почему так: print(*names, sep="\n") — то же самое, что print("Молоко", "Хлеб", "Сахар", sep="\n"), только без ручного перечисления элементов. Это самый короткий способ вывести список построчно без явного for.
Пример 3. Распаковка кортежа в цикле
Вместо item[0] и item[1] сразу называем поля по смыслу.
# 03_unpacking.py
products = [("Кофе", 127.99), ("Чай", 52.49), ("Шоколад", 81.99)]
for name, price in products:
print(name, "стоит", price)
# Кофе стоит 127.99
# Чай стоит 52.49
# Шоколад стоит 81.99
Что происходит: на каждой итерации Python распаковывает очередной кортеж в две переменные — name и price. Число имён слева должно совпадать с числом элементов кортежа, иначе будет ValueError.
Пример 4. Фильтрация списка кортежей
Фильтрация — это цикл плюс условие внутри: элементы, не прошедшие проверку, просто не выводятся.
# 04_filtering.py
stock = [("апельсин", 5), ("вишня", 12), ("киви", 18), ("ананас", 7)]
print("Товары с остатком больше 10:")
for name, amount in stock:
if amount > 10:
print(f" {name:<10} остаток: {amount}")
# Товары с остатком больше 10:
# вишня остаток: 12
# киви остаток: 18
Что происходит: условие проверяет второе поле каждого кортежа. "апельсин" и "ананас" не удовлетворяют условию amount > 10 и пропускаются — исходный список stock при этом не меняется.
Пример 5. Денежный формат .2f
Для цен и сумм всегда нужны ровно два знака после точки — иначе вывод получается неаккуратным или обманчиво точным.
# 05_money_format.py
price = 127.9
print(f"{price:.2f}")
# 127.90
print(f"{1234.5:.2f}")
# 1234.50
print(f"{0.1 + 0.2:.2f}")
# 0.30 — .2f сглаживает погрешность двоичной арифметики float
Почему так: 0.1 + 0.2 из-за двоичного представления float даёт 0.30000000000000004, но .2f округляет до нужного количества знаков при выводе, поэтому для денег такой формат — не только про красоту, но и про предсказуемость.
Пример 6. Таблица с фиксированной шириной колонок
f-strings задают ширину поля и выравнивание: < — влево (для текста), > — вправо (для чисел).
# 06_fixed_width.py
products = [("Кофе", 127.99), ("Чай", 52.49), ("Шоколад", 81.99)]
print(f"{'Название':<12} | {'Цена':>8}")
print("-" * 23)
for name, cost in products:
print(f"{name:<12} | {cost:>8.2f}")
# Название | Цена
# -----------------------
# Кофе | 127.99
# Чай | 52.49
# Шоколад | 81.99
Что происходит: ширина 12 и 8 здесь подобрана под конкретные данные вручную. Если название окажется длиннее 12 символов, колонка "поедет" — эту проблему решает динамическая ширина в следующем примере.
Пример 7. Динамическая ширина колонки
Если длины названий заранее не известны, сначала находят максимальную длину, а потом используют её как ширину поля.
# 07_dynamic_width.py
catalog = [("Молоко", 3), ("Подсолнечное масло", 5), ("Хлеб", 1)]
name_width = 0
for name, quantity in catalog:
if len(name) > name_width:
name_width = len(name)
name_width += 2
for name, quantity in catalog:
print(f"| {name:<{name_width}} | {quantity:>5} |")
# | Молоко | 3 |
# | Подсолнечное масло | 5 |
# | Хлеб | 1 |
Что происходит: {name:<{name_width}} — ширина сама подставляется из переменной. Такая таблица не "ломается" независимо от того, насколько длинными окажутся названия товаров.
Пример 8. Накопление итога и среднего
Отчёты почти всегда требуют сумму и среднее значение по коллекции.
# 08_totals.py
salaries = [("Иван", 1600), ("Мария", 2000), ("Сергей", 2400)]
total = 0
for name, salary in salaries:
total += salary
average = total / len(salaries)
print("Сумма зарплат:", total)
print("Средняя зарплата:", round(average, 2))
# Сумма зарплат: 6000
# Средняя зарплата: 2000.0
Почему так: total обязательно инициализируется нулём до цикла — если забыть это сделать, будет NameError, а если объявить total = 0 внутри цикла, сумма будет сбрасываться на каждой итерации. Деление на len(salaries) безопасно только тогда, когда список не пуст.
Пример 9. Вложенная структура: клиент с покупками
В задачах про клиентов каждая запись хранит вложенный список покупок — приходится обходить два уровня: клиентов и покупки внутри клиента.
# 09_nested.py
clients = [
("Иван", 10, [("Кофе", 100.0), ("Чай", 200.0)]),
("Анна", 15, [("Шоколад", 150.0), ("Печенье", 50.0)]),
]
for name, discount, purchases in clients:
subtotal = 0
for item_name, item_price in purchases:
subtotal += item_price
final_price = subtotal * (1 - discount / 100)
print(f"{name}: сумма покупок {subtotal:.2f}, скидка {discount}%, к оплате {final_price:.2f}")
# Иван: сумма покупок 300.00, скидка 10%, к оплате 270.00
# Анна: сумма покупок 200.00, скидка 15%, к оплате 170.00
Что происходит: внешний цикл распаковывает каждого клиента на имя, скидку и список покупок; внутренний цикл суммирует цены покупок именно этого клиента. subtotal = 0 стоит внутри внешнего цикла — иначе сумма покупок одного клиента "прилипла" бы к следующему.
Пример 10. Три ошибки в одном фрагменте
Собираем частые промахи практикума на маленьком примере, чтобы увидеть разницу между рабочим и ломающимся кодом.
# 10_common_mistakes.py
data = [("вишня", 12), ("ананас", 7)]
# лучше так: распаковка вместо индексов — сразу видно, что есть что
for name, amount in data:
print(name, amount)
# вишня 12
# ананас 7
total = 0
for _, amount in data:
total += amount # total объявлен один раз до цикла — суммирует всё верно
print("Итог:", total)
# Итог: 19
Почему так: item[0]/item[1] вместо распаковки работает, но в большой таблице с пятью-шестью полями легко перепутать индексы местами — ошибка при этом не выдаст исключение, а просто напечатает не те данные. Символ _ — соглашение "это значение мне не нужно", здесь используется вместо имени name, потому что в сумме участвует только amount.
Что делать дальше
Полные условия всех семи задач практикума — в заданиях, готовые решения — в решениях. Частые промахи разобраны подробнее в ошибках.