💻 Примеры кода

⚡ Работа со списком кортежей на одном экране

products = [("Кофе", 127.99), ("Чай", 52.49)]

for name, price in products:            # распаковка кортежа
    if price > 100:                      # фильтрация
        print(f"{name:<10} | {price:>8.2f}")   # таблица с выравниванием

print(*[n for n, _ in products], sep="\n")    # печать без цикла
ЗадачаПриёмПример
Обойти строки таблицыраспаковка в forfor name, price in products
Отобрать часть строкif внутри циклаif price > 100
Вывести без циклаprint(*items, sep=...)print(*names, sep="\n")
Колонка с выравниваниемширина в f-stringf"{name:<10}"
Деньги с двумя знаками.2ff"{price:.2f}"
Топ-3 ошибки: путать поля кортежа по индексу вместо распаковки · забыть .2f для денег · не обнулять накопитель суммы между клиентами.

Все задачи практикума 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] — его поля по индексу. В следующих примерах вместо индексов используется распаковка — она читается понятнее.

Оператор * распаковывает список прямо в аргументы 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 округляет до нужного количества знаков при выводе, поэтому для денег такой формат — не только про красоту, но и про предсказуемость.

⚠️ Проверить по документации: представление чисел с плавающей точкой — Floating-Point Arithmetic: Issues and Limitations.

Пример 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.

Что делать дальше

Полные условия всех семи задач практикума — в заданиях, готовые решения — в решениях. Частые промахи разобраны подробнее в ошибках.