Содержание
Памятка: ключевые моменты о неизменяемых типах в Python
- Неизменяемые типы: int, float, str, tuple, frozenset, bytes.
- Изменяемые типы: list, dict, set, bytearray.
- Неизменяемые объекты нельзя изменить после создания.
- Любая операция с неизменяемым объектом создает новый объект.
- Кортеж — неизменяемый аналог списка.
- Строки поддерживают срезы, но не поддерживают присваивание по индексу.
- Функция id() возвращает адрес объекта в памяти.
- Функция type() возвращает тип объекта.
- Неизменяемые объекты могут быть ключами словаря.
- Изменяемые объекты не могут быть ключами словаря.
- Числа (int, float) всегда неизменяемы.
- При присваивании нового значения переменной меняется ссылка, а не объект.
Изменяемые и неизменяемые объекты
Как известно, объектом в Python является абсолютно все, а каждый объект относится к какому-либо типу данных. Типы данных бывают изменяемые и неизменяемые (англ. mutable и immutable). К неизменяемым относятся целые числа (int), числа с плавающей запятой (float), булевы значения (bool), строки (str), кортежи (tuple). К изменяемым — списки (list), множества (set), байтовые массивы (byte arrays) и словари (dict).
Типы
Значение некоторых объектов может измениться. Объекты, чье значение может измениться, называются изменяемыми; объекты, значение которых неизменяемо после их создания, называются неизменяемыми … Изменчивость объекта определяется его типом; например, числа, строки и кортежи неизменны, а словари и списки изменчивы.
Целые числа (int)
Давайте определим переменную x, имеющую значение 10. Встроенный метод id() используется для определения местоположения x в памяти, а type() используется для определения типа переменной. Когда мы пытаемся изменить значение x, оно успешно изменяется.
Стоит заметить, что адрес памяти тоже изменяется. Так происходит потому, что фактически мы не изменили значение x, а создали другой объект с тем же именем x и присвоили ему другое значение. Мы связали имя x с новым значением. Теперь, когда вы вызываете x, он будет выводить новое значение и ссылаться на новое местоположение.
Числа с плавающей запятой (float)
У нас есть переменная x типа float. Используя функцию id(), мы можем узнать ее адрес. Если мы попробуем заменить элемент с индексом 1, то получим TypeError. Как и в предыдущих примерах видим, что float не поддерживает модификацию элемента.
Если же мы обновим float, переопределив его, то при вызове получим новое значение и новый адрес.
Строки (str)
То же самое верно и для строкового типа данных. Мы не можем изменить существующую переменную, вместо этого мы должны создать новую с тем же именем.
В данном примере мы определили строковую переменную x, но допустили ошибку в слове и теперь хотим исправить «ю» на «и». Однако мы получаем TypeError. Это показывает, что строковые объекты не подлежат обновлению.
Кортежи (tuple)
Давайте разберем кортежи. Мы определили кортеж с 4 значениями. Воспользуемся функцией id() для вывода его адреса. Если мы захотим изменить значение первого элемента, то получим ошибку TypeError. Это означает, что кортеж не поддерживает присвоение или обновление элементов.
С другой стороны, мы можем обновить весь кортеж, задав его с нуля. После этого мы увидим новые значения элементов кортежа и новый адрес.
Списки (list)
Определим список с именем x и добавим в него некоторые значения. После этого обновим список: присвоим новое значение элементу с индексом 1. Можем заметить, что операция успешно выполнилась.
Вышеописанные действия являются простым и базовым примером модификации. Чтобы проверить изменчивость на более глубоком уровне, давайте рассмотрим тот же пример с небольшими изменениями.
Создадим новое имя y и свяжем его с тем же объектом списка. А теперь проверим, совпадает ли x с y. Нам вернется True. Кроме того, x и y имеют одинаковые адреса памяти.
Если мы теперь вызовем y, то получим тот же список, что и при вызове x. Хотя непосредственно в y мы ничего не добавляли. Это означает, что по сути мы обновляем один список объектов, у которого есть два разных имени: x и y. Оба они одинаковы и имеют один адрес в памяти даже после модификации.
Словари (dict)
dict = {‘Name’:’Алиса’, ‘Age’:27, ‘Job’:’Senior Python Developer’} dict # Получим {‘Name’: ‘Алиса’, ‘Age’: 27, ‘Job’: ‘Senior Python Developer’} dict[‘Name’], dict[‘Age’], dict[‘Job’] # (‘Алиса’, 27, ‘Senior Python Developer’)
Давайте изменим какое-нибудь значение в нашем словаре. Например, обновим значение для ключа Name. Выведем обновленный словарь. Значение изменилось. При этом сами ключи словаря неизменяемы.
Функции id() и type()
Разобраться с изменяемостью типов данных нам помогут встроенные функции и операторы Python.
Встроенный метод id() возвращает идентификатор объекта в виде целого числа. Это целое число обычно относится к месту хранения объекта в памяти. Встроенная функция type() возвращает тип объекта.
Что касается операторов, для определения идентичности переменных мы можем использовать ==, а is используется для определения того, указывают ли переменные на один и тот же объект. Почитать подробнее об отличиях этих операторов можно здесь.
Создадим переменную z путем присвоения ей в качестве значения переменной x. Используя оператор is, мы обнаружим, что обе переменные указывают на один и тот же объект и, соответственно, имеют одинаковые идентификаторы.
Создание строки String
Когда я начал изучать Python, меня не слишком волновало как происходит конкатенации строк. Я часто использовал длинные цепочки строительного кода типа такого:
Однако, если мы посмотрим на id каждой строки во время создание всего предложения мы увидем:
Видно на каждом этапе создание предложения создается новая строка. Что может быть иногда не эффективно с точки зрения производительности. Возможно в подобной ситуации предпочтительно использовать изменяемый тип (например список):
Использование списков в данной ситуации будет обходится дешевле с точки зрения производительности, потому что вы можете изменить размер объекта на лету.
С другой стороны, размер неизменяемых типов заранее известен в памяти, что делает их более быстрыми во время доступа к ним (Более подробное можно почитать тут: Кортежи имеют тенденцию работать лучше, чем списки – Tuples tend to perform better than lists).
Неизменность
Еще одним преимуществом неизменяемых типов является гарантия того, что эти объекты никогда не изменятся.
Изменяемые типы могут неожиданно быть изменены где-либо в вашей программе:
- Изменяемые аргументы по умолчанию, общий анти-шаблон
- Сложные объекты – Compound objects
- Несколько потоков, изменяющих объект (хотя вы можете использовать блокировку)
- Изменчивые объекты / общее состояние (ООП) могут привести к условиям гонки (пример)
Дальнейшее чтение: 5 Benefits of Immutable Objects Worth Considering for Your Next Project
Списки и кортежи: наглядный пример изменяемых и неизменяемых объектов
Давайте по отдельности определим список и кортеж. Убедимся, что в кортеже есть значение типа список, а в списке есть значение типа кортеж.
Нулевой элемент кортежа – список. Давайте попробуем изменить какой-то из элементов списка, указав его индекс. Например, можно поменять в нулевом элементе кортежа (т.е. в списке) нулевой элемент. Нам успешно удается это сделать, потому что список – изменяемый объект, даже если он находится в кортеже.
Если же, наоборот, кортеж находится в списке, то вы не сможете поменять элемент этого кортежа, хотя он и находится в изменяемом списке. Ведь сам кортеж неизменяем. Поэтому такие преобразования невозможны.
Небольшое дополнение
Обратите внимание, что неизменность иногда не гарантируется на 100%. Например, вы можете иметь кортеж со списком внутри него:
>>> a = (1, [2, 3], 4) >>> a[0] = 2 Traceback (most recent call last): File «<stdin>», line 1, in <module> TypeError: ‘tuple’ object does not support item assignment >>> a[1].append(3.5) >>> a (1, [2, 3, 3.5], 4) </module></stdin>
Часто задаваемые вопросы о неизменяемых типах данных в Python
Вопрос: Какие типы данных в Python являются неизменяемыми?
Ответ: К неизменяемым типам относятся int, float, str, tuple, frozenset, bytes.
Вопрос: Почему строки в Python неизменяемы?
Ответ: Неизменяемость строк повышает безопасность и производительность, позволяя использовать их в качестве ключей словарей.
Вопрос: Можно ли изменить элемент кортежа?
Ответ: Нет, кортеж неизменяем, но если он содержит изменяемый объект (например, список), то этот объект можно изменить.
Вопрос: В чем разница между списком и кортежем?
Ответ: Список (list) изменяем, кортеж (tuple) неизменяем. Кортежи занимают меньше памяти.
Вопрос: Является ли число с плавающей запятой неизменяемым?
Ответ: Да, float — неизменяемый тип данных.
Вопрос: Что такое frozenset?
Ответ: Frozenset — это неизменяемая версия множества (set).
Вопрос: Как проверить, является ли объект неизменяемым?
Ответ: Используйте функцию hash(): если объект хешируем, он, скорее всего, неизменяем.
Вопрос: Можно ли использовать список в качестве ключа словаря?
Ответ: Нет, так как списки изменяемы и не хешируются. Используйте кортеж.
Вопрос: Что происходит при попытке изменить строку?
Ответ: Создается новый объект строки, исходная строка остается неизменной.
Вопрос: Какие преимущества у неизменяемых типов?
Ответ: Безопасность в многопоточных приложениях, возможность использования в качестве ключей, предсказуемость.
























