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