💻 Примеры кода: кортежи

⚡ Шпаргалка: кортежи

point = (10, 20)
x, y = point
print(f"x={x}, y={y}")

first, *middle, last = (1, 2, 3, 4)
print(first, middle, last)
ТемаЧто помнить
Созданиезапятая делает кортеж: (5,) — кортеж, (5) — просто число
Изменениенельзя — item[0] = x даёт TypeError; новый набор — через срезы и +
Переборfor, enumerate(start=...), распаковка пар прямо в заголовке цикла
Методытолько count() и index() — index() бросает ValueError, если элемента нет
Распаковкаa, *middle, b = values — остаток middle всегда list, даже из кортежа
Топ-3 ошибки: (5) вместо (5,) — получится int, а не tuple · попытка изменить элемент (items[0] = 10) — TypeError · распаковка не той длины (a, b = (1, 2, 3)) — ValueError.

Примеры идут от базовых свойств кортежа (создание, неизменяемость, операции) к прикладным задачам практикума и домашнего задания. Урок разбирает не одну задачу, а полную картину темы — вместе с ограничением source «не используйте неизученные коллекции», которое объясняет, почему уникальность и фильтрация здесь собираются вручную, а не через set(). Весь вывод в комментариях получен запуском кода.

Пример 1. Создание кортежей и неизменяемость

Ключевой признак кортежа — запятая, а не скобки. Скобки нужны только для наглядности и группировки.

📄 examples/tuple_creation.py
many_values = (1, 2, 3, "apple", True)
empty_tuple = ()
single_value = (5,)
not_a_tuple = (5)          # это просто число в скобках
packed_tuple = 1, 2, 3      # скобки вообще не обязательны

print(many_values)
print(empty_tuple)
print(single_value, type(single_value))   # (5,) <class 'tuple'>
print(not_a_tuple, type(not_a_tuple))     # 5 <class 'int'>
print(packed_tuple, type(packed_tuple))   # (1, 2, 3) <class 'tuple'>

numbers = (10, 20, 30)
try:
    numbers[1] = 40
except TypeError as error:
    print("TypeError:", error)
    # 'tuple' object does not support item assignment

updated_numbers = numbers[:1] + (40,) + numbers[2:]
print(updated_numbers)   # (10, 40, 30) — не изменили, а собрали новый кортеж

Что происходит: запятая — единственное, что делает выражение кортежем; скобки — просто визуальная подсказка (кроме пустого (), где скобки обязательны). Изменить элемент по индексу нельзя в принципе — вместо этого через срезы и + собирается новый кортеж с нужным значением.

Пример 2. Операции с кортежами и преобразование в список

Многие операции у кортежа такие же, как у списков и строк: сложение, повторение, проверка наличия, длина, поэлементное сравнение.

📄 examples/tuple_operations.py
tuple1 = (1, 2)
tuple2 = (3, 4)

print(tuple1 + tuple2)              # (1, 2, 3, 4)
print(tuple1 * 3)                    # (1, 2, 1, 2, 1, 2)
print(2 in tuple1)                   # True
print(len(tuple1))                    # 2
print((1, 2, 3) == (1, 2, 4))        # False — сравнение поэлементное

values = [1, 2, 3]
as_tuple = tuple(values)
as_list = list(as_tuple)
print(as_tuple, type(as_tuple))       # (1, 2, 3) <class 'tuple'>
print(as_list, type(as_list))          # [1, 2, 3] <class 'list'>

Почему так: tuple() и list() — не «превращение» одного объекта в другой, а создание нового объекта нужного типа из содержимого исходного. Переключаются между ними именно тогда, когда нужно один раз изменить данные (list()), а затем снова зафиксировать результат (tuple()).

Пример 3. Карточка пользователя: распаковка в именованные переменные

📄 examples/tuple_profile.py
user = ("Alice", 29, "Python")
name, age, language = user

print(f"Имя: {name}")
print(f"Возраст: {age}")
print(f"Язык: {language}")

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

Пример 4. Перебор: for, распаковка пар, enumerate()

Кортеж — итерируемый объект, поэтому его перебирают так же, как список или строку. Если элементы сами являются кортежами (парами), их можно распаковать прямо в заголовке цикла.

📄 examples/tuple_iteration.py
scores = (10, 20, 30, 40)
for score in scores:
    print(score)

pairs = ((1, "apple"), (2, "banana"), (3, "cherry"))
for number, fruit in pairs:
    print(f"Число: {number}, Фрукт: {fruit}")

languages = ("Python", "Java", "C++")
for index, language in enumerate(languages, start=1):
    print(f"{index}: {language}")

print(tuple(enumerate(languages)))
# ((0, 'Python'), (1, 'Java'), (2, 'C++')) — enumerate сам по себе не печатается как список пар

Что важно: enumerate(languages, start=1) нумерует элементы с единицы для вывода, но сам объект enumerate внутри всегда считает с нуля — это видно, если обернуть его в tuple() без start.

Пример 5. Уникальные элементы без неизученных коллекций

📄 examples/unique_tuple.py
numbers = (1, 2, 3, 2, 1, 4, 5, 3, 6)
unique_numbers = tuple()

for number in numbers:
    if number not in unique_numbers:
        unique_numbers += (number,)

print("Уникальные элементы:", unique_numbers)

Это решение следует ограничению из исходного задания: не использовать неизученные коллекции (set появится позже, в уроке 32, и решит ту же задачу короче).

Пример 6. Элементы выше среднего

📄 examples/above_average.py
numbers = (10, 15, 20, 25, 30)
average = sum(numbers) / len(numbers)
greater_than_average = tuple()

for number in numbers:
    if number > average:
        greater_than_average += (number,)

print("Элементы больше среднего:", greater_than_average)

Пример 7. Возрастающая прогрессия (задача из домашнего задания)

В новый кортеж попадают только элементы, каждый из которых больше всех предыдущих — то есть каждый новый «рекорд» по ходу перебора.

📄 examples/increasing_progression.py
numbers = (3, 7, 2, 8, 5, 10, 1)
increasing = tuple()
current_max = None

for number in numbers:
    if current_max is None or number > current_max:
        increasing += (number,)
        current_max = number

print("Кортеж по возрастанию:", increasing)   # (3, 7, 8, 10)

Как проверить себя: 2, 5 и 1 не попали в результат — каждое из них меньше, чем уже встреченный максимум (7, 8 и 10 соответственно). current_max = None в начале нужен, чтобы первый элемент попал в результат всегда, независимо от его значения.

Пример 8. Сравнение соседних элементов

📄 examples/neighbor_compare.py
numbers = (10, 20, 15, 30, 25, 35)

for index, value in enumerate(numbers[:-1]):
    next_value = numbers[index + 1]
    if value > next_value:
        print(f"{value} больше, чем {next_value}")

Срез numbers[:-1] защищает от выхода за границы: последний элемент не сравнивается с несуществующим следующим.

Пример 9. Индексы повторяющихся элементов

📄 examples/repeated_indexes.py
numbers = (1, 2, 3, 4, 2, 5, 3, 6, 4, 2, 9)
processed = tuple()

for number in numbers:
    if numbers.count(number) > 1 and number not in processed:
        indexes = tuple()
        for index, current in enumerate(numbers):
            if current == number:
                indexes += (index,)
        processed += (number,)
        print(f"Индексы элемента {number}:", *indexes)

Что происходит: numbers.count(number) > 1 — это единственное место в примерах, где нужен метод count(); processed не даёт напечатать один и тот же элемент дважды, если он повторяется больше двух раз.

Пример 10. Распаковка остатка через *

📄 examples/star_unpacking.py
numbers = (3, 7, 2, 8, 5, 10, 1)
first, *middle, last = numbers

print("Первый:", first)
print("Середина:", middle)     # [7, 2, 8, 5, 10] — список, не кортеж
print("Последний:", last)
Проверить по документации: звёздочная распаковка полезна не только с кортежами, но и с другими итерируемыми объектами; это общий синтаксис Python.

Пример 11. Ловушка: изменяемый объект внутри неизменяемого кортежа

Сам кортеж неизменяем, но ничто не мешает ему хранить изменяемый объект — например, список. Нельзя заменить список внутри кортежа на другой, но можно изменить содержимое этого списка.

📄 examples/nested_mutable.py
data = ([1, 2], "items")
data[0].append(3)     # список внутри кортежа изменился
print(data)             # ([1, 2, 3], 'items')

try:
    data[0] = [9, 9]    # а вот заменить сам элемент кортежа нельзя
except TypeError as error:
    print("TypeError:", error)
⚠️ Проверить по документации: это следствие модели объектов Python — неизменяемость кортежа касается только того, какие объекты в нём лежат по каким позициям, а не того, изменяемы ли сами эти объекты.