Об’єкти в Python

В інтерактивному інтерпретаторі Python запишемо інструкцію:

>>> a = 39

У звичайному випадку, в мові програмування даний запис можна прочитати так: змінній a присвоїти цілочисельне значення 39. Символом = тут позначається оператор присвоєння.

Змінні в Python - імена-посилання на об’єкти.

Наприклад, у записі a = 39, a - посилання на об’єкт (екземпляр класу int) - ціле число 39.

Виконаємо ще одне присвоєння:

>>> b = a

Запис b = a змушує ім’я b посилатися на той самий об’єкт, що й ім’я a. Перевіримо це твердження:

>>> a is b
True

Операція a is b перевіряє, чи вказують обидва імені на один і той же об’єкт.

Оператор is перевіряє об’єкти на ідентичність:

PEP8 (https://www.python.org/dev/peps/pep-0008/#programming-recommendations) рекомендує використовувати оператор is для порівняння з singleton-об’єктами (існують в єдиному екземплярі), наприклад, для None:

if foo is not None:
    pass

Перевірку на ідентичність можна виконати також за допомогою функції id():

>>> id(a) == id(b)
True
>>> id(a)
140713044069216
>>> id(b)
140713044069216

Функція id() повертає унікальний ідентифікатор для зазначеного об’єкта.

Ідентифікатор - це ціле число, яке гарантовано є унікальним та постійним для цього об’єкта протягом його життя.

Для порівняння ідентифікаторів об’єктів ми використали оператор порівняння ==.

Проілюструємо вищенаведені відомості ще на одному прикладі.

>>> a = 3
>>> b = 3
>>> id(3) == id(a) == id(b)
True
>>> id(3)
140713044068064
>>> id(a)
140713044068064
>>> id(b)
140713044068064
>>> c = 257
>>> d = 257
>>> id(257) == id(c) == id(d)
False
>>> id(257)
1980287707152
>>> id(c)
1980287707408
>>> id(d)
1980287707120
>>>

Ідентичність об’єкта асоціюється з адресою об’єкта в пам’яті (для реалізації CPython - найбільш поширена, еталонна реалізація інтерпретатора мови програмування Python) і буде різною для кожного запуску програми, окрім деяких об’єктів, які мають постійний унікальний ідентифікатор, таких, як цілі числа від -5 до 256 включно.

Коли ви створюєте об’єкт типу int у цьому діапазоні, ви, фактично, просто отримуєте посилання на вже існуючий об’єкт, а не на новий об’єкт. А от, наприклад, для числа 257 буде створений новий об’єкт (Цілі об’єкти).

Інтерпретатор Python оптимізований так, що невеликі цілі числа представлені одним об’єктом - це зроблено з метою поліпшення продуктивності. Для великих чисел це вже не виконується.

Інші реалізації Python, такі як Jython або IronPython, можуть мати іншу реалізацію функції id(). Тому, наведені приклади є особливостями реалізації інтерпретатора, а не мовною особливістю Python.

У стандартній реалізації CPython (написаний на C) є багато заздалегідь визначених об’єктів, включаючи кілька малих цілих чисел та кілька одиничних символів ASCII.

Реальний код повинен бути незалежним від деталей реалізації. Тому, для порівняння чисел, не варто використовувати перевірку на ідентичність (різні об’єкти можуть мати одне значення). Використовуйте a == b, щоб дізнатися чи рівні числа. Наприклад:

>>> x = 39
>>> y = 39
>>> x == y # має бути два різних об'єкти з однаковими значеннями: порівняння
True
>>> x is y # той самий об'єкт: кешування
True
>>>

У цьому прикладі змінні x і y повинні бути рівні (==, одне і те ж значення), але не еквівалентні (is, один і той же об’єкт). Однак, через те, що малі цілі числа і рядки кешуються і використовуються повторно, оператор is повідомляє, що імена-змінні посилаються на один і той же об’єкт.

Отже, коли ви запитаєте Python, чи дійсно -5 is -5, це, безумовно, буде True, тому що обидва екземпляри є одним і тим же екземпляром, тоді, як поза діапазоном [-5, 256], CPython може створювати і, ймовірно, створює нові екземпляри цих цілих чисел.

Проілюструємо вищесказане на прикладі.

>>> a = 257
>>> b = 257
>>> a is b
False
>>> id(a) == id(b)
False
>>> id(a)
1624253033232
>>> id(b)
1624253032464
>>> 257 is 257
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
True
>>> id(257) == id(257)
True
>>>

Чому 257 is 257 дає значення True? Це пов’язано з тим, що Python виконує цю інтерактивну інструкцію як єдиний блок (в реалізації CPython). Під час компіляції цього твердження CPython побачить, що у вас є два однакові літерали і буде використовувати одне і те ж представлення 257.

У мові програмування Python літерал – це вираз (або константа), що створює (генерує) об’єкт. Якщо в тексті програми зустрічається літерал, то для цього літералу створюється окремий об’єкт деякого типу. Тобто, генерується відповідний код, що створює об’єкт, який містить значення цього літералу. Наприклад, запис a = 5, можна прочитати як за допомогою числового літералу 5 створений цілочисельний об’єкт зі значенням 5, на який посилається ім’я a.

У мовах програмування із суворою типізацією необхідно вказувати як тип числа, так і те, що створюваний об’єкт сам є числом. Але в Python такої необхідності немає, інтерпретатор Python сам, на основі аналізу літералів чисел, здатний зрозуміти, що перед ним: число або ні, ціле або з плаваючою крапкою, двійкове або шістнадцяткове тощо.

Приклади літералів:

3 # числовий літерал
2.48 # числовий літерал
"some text" # рядковий літерал
[3, 5, 7, 9] # літерал списку
{'a': 1, 'b': 2, 'c': 3} # літерал словника

Ще трішки про виконання коду в інтерпретаторі CPython:

>>> id(333)
2609261210384
>>> id(334)
2609261210384
>>> id(335)
2609261210384
>>> id(333), id(334), id(335) 
(2609261210384, 2609261209872, 2609231362288)

Такі результати можна пояснити, звернувшись до документації Python для функції id(): Два об’єкти з періодами життя, що не перекриваються, можуть мати однакове значення id().

Іншими словами, оскільки ми не зберігаємо жодних посилань на ціле число 333, його об’єкт негайно видаляється з пам’яті після друку в інтерактивному інтерпретаторі.

Потім ми створюємо об’єкт 334, і йому видається така сама адреса в пам’яті. Так як немає посилань на ціле число 334, його об’єкт також видаляється з пам’яті. При створенні об’єкта 335 йому знову видається та сама адреса в пам’яті і т.д.

При виконанні інструкції єдиним блоком, ми спотерігаємо різні значення id для поданих цілих об’єктів.

Усі приклади коду тестувались в Python 3.8.


Для усіх, хто цікавиться програмуванням мовою Python.