Перевод статьи Mutable vs Immutable Objects in Python.
Все значения в Python это объекты. Объекты делятся на изменяемые и неизменяемые.
Каждая переменная ссылается на экземпляр объекта. При создании, объекту присваивается уникальный идентификатор (object id) и тип объекта. Тип объекта не меняется после создания, но может изменится состояние объекта. Изменяемые объекты меняют своё состояние после создания, а неизменяемые сохраняются в том виде в котором были созданы.
Встроенные неизменяемые типы: int
, float
, bool
, str
, tuple
, unicode
. Встроенные изменяемые типы list
, set
, dict
. Пользовательские классы обычно изменяемы. Для имитирования неизменяемости переопределите методы изменения и удаления значений чтобы они возвращали исключение.
Чтобы узнать ссылается ли переменная на изменяемый или неизменяемый рассмотрим подробнее функции id()
и type()
.
Функции id и type
Встроенная функция id()
возвращает числовой идентификатор объекта. Обычно это число соответствует месту нахождения объекта в памяти, однако это относится к особенностям реализации интерпретатора Python и зависит от платформы. Оператор is
сравнивает идентификаторы двух объектов.
Встроенная функция type()
возвращает тип объекта. Рассмотрим два примера.
1 2 3 4 5 6 7 8 9 |
# Пример 1 >>> x = "Holberton" >>> y = "Holberton" >>> id(x) 140135852055856 >>> id(y) 140135852055856 >>> print(x is y) '''comparing the types''' True |
1 2 3 4 5 6 7 |
# Пример 2 >>> a = 50 >>> type(a) <class: ‘int’> >>> b = "Holberton" >>> type(b) <class: 'string'> |
Проверим, с помощью этих функций, какие типы являются изменяемыми, а какие нет.
Изменяемые и неизменяемые объекты
Изменяемые объекты могут изменять свое состояние или содержимое, неизменяемые не могут. Практический пример проверки на изменяемость:
1 2 |
x = 10 y = x |
Мы создали объект типа int
. Идентификаторы x
и y
ссылаются на один объект.
1 2 |
id(x) == id(y) id(y) == id(10) |
Выполним простую операцию:
1 |
x = x + 1 |
Теперь
1 2 |
id(x) != id(y) id(x) != id(10) |
Ссылка находящаяся в переменной x
изменилась. Изменилась именно ссылка, не сам объект, это показывает изменение идентификатора объекта. Объект 10
не изменился, переменная y
продолжает ссылаться на него. Неизменяемые объекты не меняются после создания.
В случае изменяемых объектов
1 2 |
m = list([1, 2, 3]) n = m |
Мы создали объект типа list
. Идентификаторы m
и n
ссылаются на один объект — список из трёх элементов неизменяемого типа int
.
1 |
id(m) == id(n) |
Изменим список, удалим один элемент:
1 |
m.pop() |
Идентификатор объекта не меняется:
1 |
id(m) == id(n) |
Переменные m
и n
так же ссылаются на один и тот же объект списка после изменения списка. Список теперь содержит два элемента [1, 2]
.
Особенности изменяемых и неизменяемых объектов:
- Python обрабатывает изменяемые и неизменяемые объекты по-разному.
- Доступ к неизменяемым объектам быстрее чем к изменяемым.
- Изменяемые объекты лучше использовать когда есть необходимость изменять размер или значение объекта после создания. Неизменяемые лучше применять когда объект останется всегда в том виде в котором был создан.
- Неизменяемые объекты принципиально дороже в использовании когда их часто требуется "менять", так как любое изменение создаёт копию объекта.
Исключения в неизменяемости
В некоторых случаях неизменяемые объекты могут выглядеть как изменяемые.
Рассмотрим неизменяемый тип кортеж (tuple
). Значения кортежа не могут быть изменены после создания. Но значениями кортежа являются последовательность переменных с неизменяемой привязкой к объектам. Это ключевой момент, связка объекта и переменной (или ссылка на объект) является неизменяемой, но не сам объект с которым связана переменная.
Рассмотрим кортеж
1 |
t = (‘holberton’, [1, 2, 3]) |
Кортеж t
содержит элементы разных типов. Первый элемент это неизменяемая строка, а второй изменяемый список. Кортеж неизменяемый и не содержит методов для изменения своего содержимого. То же самое относится к строке. Но список изменяемый и содержит методы для изменения своих значений. Содержимое неизменяемого кортежа не может быть изменено, но изменяемые объекты входящие в кортеж могут менять свои значения.
Как объекты передаются в функции
При передачи параметров в функции изменяемые объекты ведут себя аналогично тому что в других языках называется "передача по ссылке". Изменения объекта внутри функции отражаются на исходном объекте:
1 2 3 4 5 6 7 8 9 10 |
def updateList(list1): list1 += [10] n = [5, 6] print(id(n)) # 17357576 updateList(n) print(n) # [5, 6, 10] print(id(n)) # 17357576 |
Неизменяемые объекты ведут себя так как будто происходит "передача по значению". Изменение объекта внутри функции не влияет на исходных объект:
1 2 3 4 5 6 7 8 9 |
def updateNumber(n): print(id(n)) n += 10 b = 5 print(id(b)) # 1552410608 updateNumber(b) # 1552410608 print(b) # 5 |