← Назад к вопросам

В чём особенность tuple?

1.0 Junior🔥 121 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Особенность tuple в Python

Кортежи (tuples) — это один из самых важных типов данных в Python. Их ключевая особенность — неизменяемость (immutability), что делает их уникальными в экосистеме Python и открывает специальные применения.

Основная особенность: неизменяемость

Tuple нельзя изменить после создания:

# Создание tuple
my_tuple = (1, 2, 3, 4, 5)

# ❌ Нельзя изменить элемент
my_tuple[0] = 999  # TypeError: 'tuple' object does not support item assignment

# ❌ Нельзя добавить элемент
my_tuple.append(6)  # AttributeError: 'tuple' object has no attribute 'append'

# ❌ Нельзя удалить элемент
del my_tuple[0]  # TypeError: 'tuple' object doesn't support item deletion

# ✅ Можно создать новый tuple
my_tuple = my_tuple + (6,)  # Создаёт новый объект
print(my_tuple)  # (1, 2, 3, 4, 5, 6)

Почему неизменяемость важна?

1. Использование как ключа в словаре

Tuple — хешируемый тип, список — нет:

# ✅ Tuple как ключ (hashable)
cache = {}
coords = (40.7128, -74.0060)  # Координаты Нью-Йорка
cache[coords] = "New York"
print(cache[(40.7128, -74.0060)])  # New York

# ❌ Список как ключ (unhashable)
try:
    cache[[40.7128, -74.0060]] = "New York"
except TypeError:
    print("Ошибка: список нельзя использовать как ключ")

# ✅ Хеш от tuple всегда одинаковый
print(hash((1, 2, 3)))  # Будет одним и тем же числом
print(hash((1, 2, 3)))  # Одно и то же

2. Безопасность в многопоточной среде

Tuple безопасен в многопоточности без блокировок:

import threading

# Tuple — безопасен
data_tuple = (1, 2, 3, 4, 5)

def read_thread():
    while True:
        print(data_tuple)  # Безопасно читать в разных потоках

# Несколько потоков могут одновременно читать
for _ in range(5):
    threading.Thread(target=read_thread, daemon=True).start()

# Список требует блокировки
from threading import Lock
data_list = [1, 2, 3, 4, 5]
lock = Lock()

def modify_list():
    with lock:  # Нужна блокировка
        data_list[0] = 999

3. Производительность и оптимизация памяти

Tuple занимает меньше памяти и быстрее создаётся:

import sys
from timeit import timeit

# Размер в памяти
my_tuple = (1, 2, 3, 4, 5)
my_list = [1, 2, 3, 4, 5]

print(f"Tuple: {sys.getsizeof(my_tuple)} bytes")
print(f"List:  {sys.getsizeof(my_list)} bytes")
# Output: Tuple: 80 bytes, List: 104 bytes

# Скорость создания
tuple_time = timeit(lambda: (1, 2, 3, 4, 5), number=1000000)
list_time = timeit(lambda: [1, 2, 3, 4, 5], number=1000000)

print(f"Tuple creation: {tuple_time}s")
print(f"List creation:  {list_time}s")
# Tuple примерно в 2 раза быстрее

4. Особенность: содержимое может быть изменяемо

Важно! Tuple сам неизменяем, но может содержать изменяемые объекты:

# Tuple содержит список
my_tuple = (1, 2, [3, 4, 5])

# ❌ Нельзя изменить сам tuple
my_tuple[0] = 999  # TypeError

# ✅ Но можно изменить содержимое списка внутри
my_tuple[2][0] = 999
print(my_tuple)  # (1, 2, [999, 4, 5])

# Такой tuple больше не хешируемый
print(hash(my_tuple))  # TypeError: unhashable type: 'list'

5. Распаковка (unpacking)

Tuple отлично подходит для распаковки:

# Функция возвращает кортеж
def get_coordinates():
    return (40.7128, -74.0060, "New York")

# Простая распаковка
lat, lon, city = get_coordinates()
print(f"{city}: {lat}, {lon}")

# Распаковка с игнорированием
lat, lon, _ = get_coordinates()

# Распаковка с захватом остатка
first, *middle, last = (1, 2, 3, 4, 5)
print(first, middle, last)  # 1 [2, 3, 4] 5

# Множественное присваивание
a, b = b, a  # Поменять значения (работает благодаря tuple)

6. Named tuples для структурированных данных

Сложные данные с понятным синтаксисом:

from collections import namedtuple

# Создание типа данных
Point = namedtuple('Point', ['x', 'y', 'z'])

# Использование
p = Point(1, 2, 3)
print(p.x, p.y, p.z)  # 1 2 3
print(p[0], p[1], p[2])  # 1 2 3 (тоже работает)

# Остаётся неизменяемым
p.x = 999  # AttributeError: can't set attribute

# Очень полезно для возврата значений
def get_user():
    User = namedtuple('User', ['id', 'name', 'email'])
    return User(1, 'Alice', 'alice@example.com')

user = get_user()
print(f"{user.name}: {user.email}")

7. Пустой tuple и singleton

# Пустой tuple
empty = ()
print(len(empty))  # 0

# Tuple с одним элементом требует запятой
single = (42)    # Это просто число, не tuple!
print(type(single))  # <class 'int'>

single = (42,)   # Это tuple!
print(type(single))  # <class 'tuple'>
print(len(single))   # 1

# Запятая критична
tuple_one = (1,)
list_one = [1]
print(len(tuple_one))  # 1
print(len(list_one))   # 1

8. Интернирование строк (оптимизация Python)

Python кеширует некоторые tuple для оптимизации:

# Малые целые числа кешируются
t1 = (1, 2, 3)
t2 = (1, 2, 3)
print(t1 is t2)  # True — один и тот же объект в памяти

# Но это не гарантировано
t3 = (1, 2, 256)
t4 = (1, 2, 256)
print(t3 is t4)  # True (может быть)
print(id(t3) == id(t4))  # Проверь на своей системе

9. Практические применения

Возврат нескольких значений:

def calculate():
    return (result, status, message)

result, status, msg = calculate()

Параметры функции (упаковка/распаковка):

def process(*args, **kwargs):  # args это tuple
    print(args)  # Автоматически tuple
    for arg in args:
        print(arg)

Кортежи как части множеств:

# Tuple в set
coordinates = {(0, 0), (1, 1), (2, 2)}
print((1, 1) in coordinates)  # True, быстро O(1)

Резюме особенностей tuple

  1. Неизменяемость — основная особенность
  2. Хешируемость — можно использовать как ключ словаря или элемент set
  3. Потокобезопасность — безопасен в многопоточности без синхронизации
  4. Производительность — быстрее списков в создании и доступе
  5. Распаковка — удобный синтаксис для работы с несколькими значениями
  6. Named tuples — структурированные данные с доступом по имени
  7. Гибкость — может содержать изменяемые объекты, но сам не меняется

Tuple — это не просто "неизменяемый список", это инструмент для безопасности, производительности и проектирования надёжного кода.