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

Является ли кортеж неизменяемым объектов?

1.2 Junior🔥 201 комментариев
#REST API и HTTP#Базы данных (NoSQL)

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

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

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

Неизменяемость кортежей в Python

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

Что означает неизменяемость кортежа

Неизменяемость кортежа означает:

  • Нельзя добавить новые элементы
  • Нельзя удалить существующие элементы
  • Нельзя изменить существующий элемент (переприсвоить ссылку)
tuple_example = (1, 2, 3)

# Попытка изменить элемент — ошибка
tuple_example[0] = 10  # TypeError: 'tuple' object does not support item assignment

# Попытка добавить элемент — ошибка
tuple_example.append(4)  # AttributeError: 'tuple' object has no attribute 'append'

# Попытка удалить элемент — ошибка
del tuple_example[0]  # TypeError: 'tuple' object doesn't support item deletion

Ловушка: кортеж с изменяемыми элементами

Хотя кортеж сам неизменяем, это не означает, что содержимое кортежа неизменяемо. Если кортеж содержит ссылки на изменяемые объекты (списки, словари, множества), эти объекты можно менять:

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

# Нельзя переприсвоить элемент
tuple_with_list[2] = [6, 7, 8]  # TypeError

# НО можно изменить сам список
tuple_with_list[2][0] = 999  # OK!
print(tuple_with_list)  # (1, 2, [999, 4, 5])

# Это изменение влияет и на другие ссылки на этот список
original_list = [3, 4, 5]
tuple1 = (1, 2, original_list)
tuple2 = (10, 20, original_list)

original_list[0] = 999
print(tuple1)  # (1, 2, [999, 4, 5])
print(tuple2)  # (10, 20, [999, 4, 5])

Хешируемость — следствие неизменяемости

Поскольку кортежи неизменяемы, они хешируемы (hashable) и могут использоваться как ключи словарей и элементы множеств:

# Кортежи как ключи словаря
coordinates = {
    (0, 0): "origin",
    (1, 2): "point A",
    (3, 4): "point B"
}
print(coordinates[(1, 2)])  # point A

# Кортежи в множестве
points = {(0, 0), (1, 2), (3, 4)}
print((1, 2) in points)  # True

# Списки не могут быть ключами
my_dict = {[1, 2]: "value"}  # TypeError: unhashable type: 'list'

Проблема: кортеж с изменяемым элементом как ключ

# Это работает
my_dict = {(1, 2, 3): "value"}
print(my_dict[(1, 2, 3)])  # value

# Это НЕ работает — кортеж содержит список
my_dict = {(1, 2, [3, 4]): "value"}  # TypeError: unhashable type: 'list'

# Но это работает, пока содержимое списка не меняется...
inner_list = [3, 4]
tuple_key = (1, 2, inner_list)

# Если пытаемся использовать как ключ до изменения:
try:
    my_dict = {tuple_key: "value"}
except TypeError as e:
    print(f"Error: {e}")  # unhashable type: 'list'

Кортеж считается неизменяемым, если все его элементы неизменяемы

# Полностью неизменяемые кортежи
immutable_tuple1 = (1, 2, 3)
immutable_tuple2 = ("a", "b", "c")
immutable_tuple3 = (1, "a", 3.14, True, None)

# Вложенные неизменяемые кортежи
immutable_tuple4 = (1, (2, 3), (4, 5))

# Все эти кортежи хешируемы
my_set = {immutable_tuple1, immutable_tuple2, immutable_tuple3, immutable_tuple4}
print(len(my_set))  # 4

# Если кортеж содержит изменяемый объект
mutable_tuple = (1, [2, 3], 4)  # Кортеж с условно "изменяемым" содержимым

# Нельзя использовать как ключ или добавить в set
my_dict = {mutable_tuple: "value"}  # TypeError
my_set.add(mutable_tuple)  # TypeError

Сравнение: кортеж vs список

# Кортеж
t = (1, 2, 3)
print(hash(t))  # Кортеж хешируется: 2528502973977326062

# Список
l = [1, 2, 3]
print(hash(l))  # TypeError: unhashable type: 'list'

# По памяти
print(t.__sizeof__())  # Обычно меньше
print(l.__sizeof__())  # Обычно больше

Определение "настоящей" неизменяемости

from typing import Tuple

# Безопасно неизменяемый кортеж (без изменяемых элементов)
def create_safe_tuple(a: int, b: int) -> Tuple[int, int]:
    return (a, b)

# Условно неизменяемый кортеж (содержит изменяемые элементы)
def create_unsafe_tuple(data: list) -> tuple:
    return (data,)

data = [1, 2, 3]
unsafe = create_unsafe_tuple(data)
data[0] = 999
print(unsafe)  # ([999, 2, 3],) — содержимое изменилось!

Практический пример

class Cache:
    def __init__(self):
        self._cache = {}  # Кортежи как ключи
    
    def get(self, params: tuple) -> any:
        """params должен быть неизменяемым кортежем"""
        return self._cache.get(params)
    
    def set(self, params: tuple, value: any):
        """Сохраняет результат для набора параметров"""
        if not isinstance(params, tuple):
            raise TypeError("params must be a tuple")
        self._cache[params] = value

cache = Cache()
cache.set(("user", 123, "profile"), {"name": "John"})
result = cache.get(("user", 123, "profile"))
print(result)  # {'name': 'John'}

Заключение

Кортеж в основном неизменяем, однако это не гарантирует неизменяемость его содержимого. Для полной безопасности используйте кортежи только с примитивными типами (int, str, float, None) или другими неизменяемыми кортежами. Помните, что хешируемость кортежа зависит от хешируемости всех его элементов, и это критично при использовании кортежей как ключей словарей или элементов множеств.

Является ли кортеж неизменяемым объектов? | PrepBro