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

Давайте углубимся в детали. Поскольку все в Python является объектом, то каждая переменная является экземпляром какого-то класса (объектом). Когда объект инстанцируется, ему присваивается уникальный id. Тип объекта определяется во время выполнения и после этого не меняется, однако состояние может меняться, если оно изменяемое. Проще говоря, изменяемый объект можно изменить после его создания, а неизменяемый – нет.

Объекты встроенных типов, таких как int, float, bool, str, tuple, unicode, являются неизменяемыми. Объекты встроенных типов, таких как list, set, dict, являются изменяемыми. Самописные классы, как правило, изменяемые. Если есть потребность сымитировать неизменяемость, нужно переопределить методы установки и удаления атрибутов класса так, чтобы они вызывали исключения.

Возникает вопрос, как узнать, что наша переменная изменяемый или неизменяемый объект. Для этого нам нужно понять, зачем нужны функции id и type.

Id() и type()

Встроенная функция id() возвращается id объекта в виде целого числа. Число обычно коррелирует с местоположением объекта в памяти, но не всегда, поскольку все зависит от реализации Python и используемой платформы. Оператор is сравнивает id двух объектов.

Встроенная функция type() возвращает тип объекта. Давайте обратимся к примеру.

''' Example 1 '''
>>> x = "Holberton"
>>> y = "Holberton"
>>> id(x)
140135852055856
>>> id(y)
140135852055856
>>> print(x is y) '''comparing the types'''
True
''' Example 2 '''
>>> a = 50
>>> type(a)
<class: ‘int’>
>>> b = "Holberton"
>>> type(b)
<class: 'string'>

Теперь мы знаем, как сравнить две простые строковые переменные, чтобы определить их тип и id. С помощью этих двух функций мы можем узнать типы объектов связанных с переменными и их изменяемость/неизменяемость.

Изменяемые и неизменяемые объекты

Итак, как мы выяснили, изменяемый объект может изменять свое состояние или содержимое, а неизменяемый – нет.

Изменяемые объекты:

list, dict, set, byte array

Неизменяемые объекты:

int, float, complex, string, tuple, frozenset (неизменяемая версия set), bytes

Пример определения изменяемости объекта:

x = 10
x = y

Мы создаем объект типа int. Id x и y указывают на один и тот же объект:

id(x) == id(y)
id(y) == id(10)

А теперь просто прибавим единицу:

x = x + 1

Теперь

id(x) != id(y)
id(x) != id(10)

Объект переменной x изменился. Объект 10 не может изменяться. Неизменяемые объекты не допускают изменений после создания.

А если объект изменяемый:

m = list([1, 2, 3])
n = m

Мы создаем объект типа list. Id переменных m и n привязаны к одному списку, в котором лежит коллекция из трех неизменяемых объектов типа int.

id(m) == id(n)

Удаление элемента из объекта списка изменяет сам объект:

m.pop()

Но id объекта не изменяется

id(m) == id(n)

Переменные m и n будут указывать на один и тот же объект списка после изменения. В объекте списка теперь лежит [1,2].

Итак, что же мы поняли из примеров выше?

  • Python обрабатывает изменяемые и неизменяемые объекты по-разному. 

  • Доступ к неизменяемым объектам осуществляется быстрее, чем к изменяемым.

  • Изменяемые объекты отлично подойдут, если вам нужно менять размер объектов, например, list, dict и т.д. 

  • Неизменяемые значения используются, когда вам нужно убедиться, что созданный вами объект никогда не будет меняться.

  • Неизменяемые объект принципиально дорого менять, поскольку для этого требуется создать копию, а изменяемые менять легко.

Исключения в неизменяемости

Не все неизменяемые объекты на самом деле являются неизменяемыми. Запутались? Позвольте мне объяснить.

Как мы говорили ранее, такие коллекции в Python, как кортежи, неизменяемы. То есть значение кортежа нельзя менять после его создания. Но значение кортежа на самом деле – это последовательность имен с неизменяемыми привязками к объектам. Главное, что нужно понимать, так это то, что сами «привязки» менять нельзя, а вот объекты на их концах – можно.

Рассмотрим кортеж t = (‘holberton’, [1, 2, 3])

Он содержит элементы разных типов данных, первый из которых – неизменяемая строка, а второй – изменяемый список. Сам со себе кортеж неизменяемый, то есть для него нет методов изменения содержимого. Строка также неизменяемая по той же причине. Но у списка есть такие методы, поэтому его можно изменить. Здесь тонкий момент, но не менее важный: «значение» неизменяемого объекта не может измениться, а вот его элементы могут.

Как объекты передаются в функции

Для нас важно знать разницу между изменяемыми и неизменяемыми типами, чтобы понимать, как они обрабатываются при передаче в функции. Эффективность использования памяти сильно зависит от выбора соответствующих объектов.

Например, если изменяемый объект вызывается по ссылке в функции, он может изменить исходную переменную. Следовательно, чтобы избежать этого, исходную переменную надо скопировать в другую переменную. Неизменяемые объекты можно передавать по ссылке, потому что их значение в любом случае не может меняться.

def updateList(list1):
    list1 += [10]
n = [5, 6]
print(id(n))                  # 140312184155336
updateList(n)
print(n)                      # [5, 6, 10]
print(id(n))                  # 140312184155336

Как видно из примера выше, мы получили доступ к списку по ссылке, поэтому смогли изменить исходный список.

Давайте посмотрим на другой пример:

def updateNumber(n):
    print(id(n))
    n += 10
b = 5
print(id(b))                   # 10055680
updateNumber(b)                # 10055680
print(b)                       # 5

В примере выше в функцию передается один и тот же объект, но значение переменных не меняется даже если их id одинаковые. Так работает передача по значению. Что же здесь происходит? Когда функция вызывает значение, передается только значение переменной, а не сам объект. Таким образом, переменная, ссылающаяся на объект, не изменяется, а сам объект изменяется, но только в пределах области видимости функции. Следовательно, изменение не видно «снаружи».


16 мая в Otus состоится открытое занятие «Работа с сетью», на котором познакомимся с HTTP, кодами статусов, методами, типами ответа, а также с протоколом wsgi. В итоге мы поймем принципы работы HTTP, научимся различать методы и статус коды. Записаться можно здесь.

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


  1. DaneSoul
    04.05.2022 18:12
    +5

    Какая-то схема у Вас монструозная и плохо читаемая получилась.
    Мне нравится вот такая классификация коллекций:
    image


  1. masai
    04.05.2022 22:44

    Объект 10 не может изменяться.

    При желании его можно изменить. :)