27

Детализирую вопрос.
В книгах по Python пишут, что все в Python есть объект.

Берем традиционный подход к изучению/обучению языкам программирования. Языки Pascal, C, C++, у них есть понятие "имя переменной" (идентификатор). С именем переменной связывается/присваивается значение. Значение хранится в памяти. Имя переменной - это способ (в исходном тексте программы) обратиться к ячейке памяти для получения значения, которое там хранится.

Не знаю верно ли высказывание (для компилируемых языков): Для переменных, например, целого типа, в момент выполнения программы на компьютере, процессор работает уже с адресами ячеек памяти, где хранятся значения переменных. Т.е. не такого, что где-то по какому-то адресу хранится имя переменной и оно связывается с адресом ячейки, в которой хранится значение.

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

a = 1
print(a)
a = "Hello"
print(a)

Но тогда как понять действие функции id()? По определению стандарта она возвращает "identify" указанного объекта. Причем, "возвращает целое, гарантированно являющееся уникальным" и постоянным для объекта на время его существования."

Тогда почему

a = 1
b = 1
print(id(a) == id(b) == id(1))  # True
print("Why?")

ведь a, b, 1 разные объекты?
По-моему вопрос получился...

Используется Python 3.6


Про значения от -5 до 256 наслышан. Имеет место быть

a = 1000000
b = 1000000
print(id(a) == id(b))  # True
print("Why?")

На счет того, что достаточно "знать об именах и т.д.". Для меня не достаточно, потому и спрашиваю у уважаемых профи. В Питоне все объект. Нет имен переменных. Отсюда и мой вопрос.

jfs
  • 52,361

4 Answers4

15

В разное время с одним и тем же объектом может соотноситься разный другой объект.

Вы хотели сказать: в разное время, одно и то же имя может ссылаться на разные объекты

Модель в Питоне простая — совершенно не нужно знать что такое адрес, ячейка памяти, указатель. Достаточно знать только об именах и объектах, к которым они привязаны.

Вот картинки, которые поясняют разницу между переменными в С подобных языках и именами в Питоне. Ярлыки и шарики достаточны, чтобы любое поведение на уровне Питона объяснить — сперва не верится что такая простая модель все крайние случаи охватывает.

ведь a, b, 1 разные объекты?

У вас только один объект (единица). Маленькие целые кэшируются в CPython. Разные имена a, b ссылаются на один и тот же объект: a is b (id(a) == id(b)). 1 это константа в исходном коде (думайте как о вызове конструктора для объекта). Подробнее об int объектах: Присваивание в Python.

a = 1000000; b = 1000000; print (id(a) == id(b)) # True

Для проверки ссылаются ли имена на одни и те же объекты, используйте a is b (здесь это равнозначно id(a) == id(b), но id может быть переопределён в общем случае).

Результат может быть True (зависит как Питон код был в байт-код скомпилирован в выбранной реализации), но не обязан быть True:

>>> a = 1000000
>>> b = 1000000
>>> print (a is b) 
False

В данном случае, каждое выражение отдельно компилируется, a и b на разные объекты ссылаются. Сравните:

>>> def f():
...     a = 1000000
...     b = 1000000
...     print(a is b)

>>> f()
True

Код функции как одно целое компилируется, поэтому a is b может (но не обязано) быть True. Один это объект или несколько может зависеть от реализации Питона (CPython, Pypy, Jython, etc) и даже конкретной версии реализации. Реальный код не должен зависеть от подобных деталей реализации. Как явно сказано по ссылке: не используйте проверку на идентичность для сравнения чисел (разные объекты могут иметь одно значение). Используйте a == b, чтобы узнать равны ли числа.

Питоне все объект. Нет имен переменных. Отсюда и мой вопрос.

Конечно в Питоне есть имена. a и b это имена в вашем вопросе.

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

Примеры:

Также, для манипуляций Питон кода, может быть полезно знать о compile(), eval(), exec() встроенных функциях, dis, uncompyle6, inspect модулях. К примеру, inspect.signature() позволяет манипулировать описанием функции как объектом. В уже упомянутой ссылке показано как саму память, занимаемую объектом в CPython реализации, можно также как обычным Питон объектом манипулировать с помощью ctypes (пример изменяет неизменяемый на уровне Питона объект типа int).

jfs
  • 52,361
  • a = 1000000 b = 1000000 print (id(a) == id(b)) # True print ("Why?") – Молодой еще May 05 '17 at 14:21
  • Спасибо за картинки. Он поясняют Вашу правильную мысль. Но они не соответствуют описанию Питона в книгах. Но они ПРАВИЛЬНЫЕ. Просто надо переписывать доки к Питону. Получается, что не все в Питоне ОБЪЕКТ. – Молодой еще May 05 '17 at 14:31
  • @Молодойеще: всё в Питоне объект. Если хотите вы можете AST (пример, macropy) или байт-кодом (byteplay) манипулировать на лету. – jfs May 05 '17 at 14:47
  • Тогда. что такое имя? Какой это объект? – Молодой еще May 05 '17 at 14:50
  • 1
    @Молодойеще: я не верю, что вы за 3 минуты, смогли прочитать и понять приведённые ссылки. Особенно на картинки в описании macrpy посмотрите. – jfs May 05 '17 at 14:54
  • Вообще-то довольно быстро и прочитать и понять. Когда знаешь что ищешь. – Молодой еще May 05 '17 at 15:15
  • 1
    @Молодойеще: мне заметно больше 3 минут потребовалось, чтобы понять, что такое macropy и как оно работает. Кстати, не стесняйтесь вопросы задавать ("что такое имя? Какой это объект?" -- это отличный вопрос (напомнило Richard Feynman. Why.). В данном случае, ответ скучный: в AST представлен типом _ast.Name), просто старайтесь ответы не забывать читать также. – jfs May 05 '17 at 15:25
11

В питоне интерпретатор оптимизирован так, что небольшие целые числа представлены одним объектом - это сделано в целях улучшения производительности. Для больших чисел это уже не выполняется.

Марк Лутц - Изучаем Python, 4-е издание, 2011, стр. 204:

>>> X = 42 
>>> Y = 42  # Должно получиться два разных объекта 
>>> X == Y
True

>>> X is Y # Тот же самый объект: кэширование в действии! True

В этом примере переменные X и Y должны быть равны (==, одно и то же значение), но не эквивалентны (is, один и тот же объект), потому что было выполнено два разных литеральных выражения. Однако из-за того, что малые целые числа и строки кэшируются и используются повторно, оператор is сообщает, что переменные ссылаются на один и тот же объект.

Xander
  • 20,499
  • 2
    1- Реализации Питона такие как CPython, Pypy, Jython могут закэшировать (intern) маленькие целые, но в общем случае Питон не обязан это делать и одинаково-корректное поведение между реализациями может быть разным (попробуйте a=-6 на CPython и Pypy). 2- "литеральные выражения" вовсе не обязаны разные объекты создавать даже для больших целых. Посмотрите явный пример для a=1000000 в моём ответе. – jfs May 07 '17 at 13:18
1
  1. переменные хранят только ссылки на объект
  2. оператор = присваивает переменной ссылку
  3. примитивные типы (литералы: числа, строки) с одинаковым значением представлены единственным объектом
0

Я думаю лучше посмотреть на иерархию классов или типов Python. Они все начинаются с базового типа, который называется object. Ну и у них есть некоторые методы, которые называются дандер методами. Начинаются и заканчиваются на двойное подчёркивание.

Тут важно ещё отметить про пространства имён. В python их много. Например функция, класс, модуль - это всё пространства имён. За одним именем что-то да хранится.

Что касается чисел, то числа - неизменяемые. То есть когда ты присваиваешь какое-то число какой-то переменной - оно кладётся в память как есть. А счётчик ссылок увеличивается на 1. Когда ты пытаешься второй раз присвоить это число какой-то переменной - оно не создаётся. Просто счётчик ссылок увеличивается ещё на 1.

Соответственно, когда ты удаляешь число - счётчик ссылок уменьшается на 1. Когда счётчик станет равным 0 - сборщик мусора удалит этот объект.

Итак почему всё в python является объектом? Потому что наследуется от класса object. Даже базовые типы типа int, str.

Почему:

a = 1
b = 1
print(id(a) == id(b) == id(1)) 

Потому, что 1 создаётся 1 раз при объявлении а = 1. И дальше а просто ссылается на 1. А когда создаётся b, 1 уже существует, так как на неё есть одна ссылка. И b просто ссылается на туже единицу.

На самом деле единица в python много где используется и если посмотреть количество ссылок на неё, то их будет много. Также True является псевдонимом для 1.

Если хочешь лучше изучить тему почитай про иерархию классов \ типов python, об областях видимости и о работе сборщика мусора.

Dezox
  • 23
  • 4
  • 1
    Это вообще никак не связано с иерахирей классов. Числа от -50 до 128 в единственном экземпляре существуют, т.е. они синглтоны фактически. Это особенность реализации CPython. Количество ссылок на них бессмысленно смотреть, эти объекты не удаляются никогда. – insolor Nov 06 '23 at 18:23
  • Почитайте по ссылке, там вообще великий текст, в частности и про этот кейс тоже https://github.com/satwikkansal/wtfpython#-how-not-to-use-is-operator – CrazyElf Nov 07 '23 at 06:24
  • Пардон, ошибся, не от -50 до 128, а от -5 до 256 включительно https://i.stack.imgur.com/DBicQ.png https://i.stack.imgur.com/uZO3P.png – insolor Nov 07 '23 at 06:36
  • Эти числа не связаны с иерархией классов python. Но вопрос, который интересовал ТС в том, почему всё в Python является объектом. Ему нужно было общее понимание, а не знание особенностей реализации CPython. Для общего понимания этими числами вообще можно пренебречь и почитать по указанным выше темам. – Dezox Nov 07 '23 at 13:09
  • "Итак почему всё в python является объектом? Потому что наследуется от класса object." - из второго не следует первое. Скорее наоборот, автором языка было принято решение, что все в Python - это объекты, поэтому они наследуются от object. Чтобы сделать, чтобы "сущность" наследовалась от object (в принципе, могла наследоваться от чего-то), она уже должна быть объектом. – insolor Nov 08 '23 at 07:00