I, Объект: Понимание изменяемых и неизменяемых объектов в Python.

Новички в Python сначала узнают, что все в Python является объектом и что некоторые из них являются либо изменяемыми, либо неизменяемыми. Для начала, что такое объект? В Python, когда вы хотите создать новую «вещь», вы создаете класс. Объект представляет собой базовую версию этого класса, или конкретно известный как экземпляр. Таким образом, хотя класс и является планом, все, что создано на его основе, считается объектом.

Таким образом, «все в Python является объектом» означает, что каждая переменная содержит экземпляр объекта. При создании объекта ему присваивается уникальный идентификатор объекта. В зависимости от типа объекта изменяемый объект можно изменить после его создания, а неизменяемый объект изменить нельзя.

Объекты встроенных типов, таких как (int, float, str, tuple), являются неизменяемыми, в то время как объекты встроенных типов, таких как (list, set, dict), являются изменяемыми. Пользовательские классы, как правило, изменяемы, но их можно настроить для имитации неизменности.

Чтобы узнать, является ли наша переменная изменяемым или неизменяемым объектом, нам нужно использовать функции id() и type().

ID и ТИП

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

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

''' Example 1 '''
>>> a = "Gary"
>>> b = "Gary"
>>> c = "Joe"
>>> id(a)
140693285555928
>>> id(b)
140693285555928
>>> id(c)
140693285741376
>>> a is b
True
>>> a is c
False
''' Example 2 '''
>>> a = 100
>>> type(a)
<class: ‘int’>
>>> b = "Hello"
>>> type(b)
<class: 'string'>

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

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

Изменяемый объект может изменить свое состояние или содержимое. Вот пример:

a = 12
a = b
id(a) == id(a) 
id(b) == id(10) 

Мы только что создали объект типа int(12). Идентификаторы a и b указывают на один и тот же объект, поскольку их идентификаторы одинаковы.

Если мы выполним простую операцию, как показано ниже. Мы видим, что a больше не указывает на тот же объект, что и b.

a = a + 5
id(a) != id(b) 
id(a) != id(12)

Объект, в котором был помечен a, заменяется другим объектом. Созданный нами объект 12 никогда не был и не может быть изменен, следовательно, объект 12 типа int является неизменяемым объектом.

Давайте переключим передачу и посмотрим на случай с изменяемыми объектами:

x = list([1, 2, 3, 4])
y = x
id(x) == id(y)

Здесь мы создали объект списка типов с переменными x и y, указывающими на один и тот же объект, который представляет собой список из 4 неизменяемых объектов.

Удаление элемента из списка с помощью функции pop() изменяет объект,

x.pop()
x = list([1, 2, 3])
id(x) == id(y)

Если мы проверим идентификатор обоих идентификаторов, x и y будут указывать на один и тот же измененный объект.

Однако Python позволяет некоторым неизменяемым объектам иметь некоторые свойства изменчивости. Примером может служить объект типа tuples.

Рассмотрим кортеж =(‘Bill’, [1, 2, 3]). Кортеж содержит неизменяемую строку и изменяемый список. Сам кортеж неизменяемый, поскольку мы можем изменить его содержимое. То же самое касается строки, поскольку у нас нет возможности ее изменить. Однако у объекта списка есть изменяемые методы, поэтому его можно изменить.

Зачем нужны изменяемые и неизменяемые объекты?

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

Передача объектов в функции

Давайте углубимся в то, как объекты передаются в функции. Вот пример передачи неизменяемого объекта в функцию:

def increment(n):
    n += 1
    return n
a = 3
increment(a)  
print(a)          # a = 3; a is still pointing to same object

Как видно из приведенного выше примера, мы вызвали номер через вызов значения. При a = 3 у нас есть переменная, ссылающаяся на значение 3 типа int, и когда мы передали ее в функцию приращения, локальная переменная n указывает на тот же самый объект int 3. Но int 3 неизменяем, поэтому мы должны создать новый объект со значением 4 с переменной n, указывающей на этот новый объект. Идентификатор a по-прежнему указывает на объект int 3.

Так можем ли мы когда-нибудь манипулировать неизменяемыми объектами при передаче их в функции? Мы можем переназначить переменную a, чтобы она указывала на наше новое возвращаемое значение 4.

def increment(n):
    n += 1
    return n
a = 3
a = increment(a)  # the return value of increment() is captured!
print(a)
a = 4   # a now points to 4

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

В случае передачи изменяемых объектов:

def add_to_list(l):
    l.append(2)
n = [1, 2]
updateList(n)
print(n)                      # [1, 2, 3]

Здесь мы вызвали список через вызов по ссылке, поэтому возможны изменения, внесенные в исходный список. Поскольку новый объект не создается, нет необходимости переназначать идентификатор n, чтобы он указывал на измененный список.