1

Можете пожалуйста объяснить как это работает:

>>> x = 5
>>> id(5)
1520660801968
>>> x += 1
>>> id(x)
1520660802000
>>> x -= 1
>>> id(x)
1520660801968
>>>

Создаётся PyObject для имени x, когда мы прибавляем к имени x 1, то создаётся новый PyObject, в результате 2 PyObject, при этом счётчик ссылок нового PyObject увеличивается на 1, а счётчик ссылок старого PyObject уменьшается на 1. Почему мы просто не можем перезаписывать старый PyObject (как работает в C, вместо имён - переменные, которые не изменяют адрес памяти), из-за этого потребление памяти в Python больше?

  • Потому что один и тот же PyObject может использоваться в разных местах программы. Если его менять, то будут неожиданно меняться числа по всей программе. Числа и строки в Python неизменяемые. – andreymal Jan 02 '21 at 17:37
  • https://docs.python.org/3/library/sys.html#sys.getrefcount – Кирилл Малышев Jan 02 '21 at 17:54

1 Answers1

4

В С вы можете сами управлять тем, где у вас скалярные значения, а где указатели. В python для того что бы сделать язык проще и решили спрятать понятие указателя от разработчика. И для этого сделали указателями все. Любая переменная - указатель. А значит изменять скалярный по природе объект по указателю нельзя, так как могут быть другие переменные ссылающиеся на тот же объект. Отсюда получили два эффекта, во первых обращение к любой переменной медленнее чем могло бы быть. Во вторых да, использование памяти в какие то моменты времени может быть выше. Но в отличие от C python сам следит за использованием памяти. Разработчику в большинстве случаев не надо об этом задумываться и освобождать память от ненужных объектов. Когда на какой нибудь объект не ссылается ни одна переменная, то сборщик мусора этот объект удаляет через некоторое время.

Очень может быть, что для небольших чисел объекты есть всегда. Если же использовать числа большего размера, то можно обнаружить другое поведение:

>>> a=590
>>> id(a)
140274437545744  # Объект 590 размещен в памяти
>>> a+=1
>>> id(a)
140274437545904  # 591 находится в новой области памяти
>>> a+=1
>>> id(a)
140274437545744  # 590 уже был удален сборщиком мусора и его адрес использован для 592
>>> a-=2
>>> id(a)
140274437545904  # Там где был 591 теперь 590
>>> a
590              # В чем можно убедится
Mike
  • 44,087
  • Cоздать новый объект проще чем проверить количество ссылок? – TigerTV.ru Jan 03 '21 at 10:49
  • @TigerTV.ru Не понял вопроса. думаю создавать объект дороже. Но не могу себе представить ситуацию при которой проверка счетчика ссылок могла бы заменить собой создание объекта – Mike Jan 03 '21 at 10:52
  • 1
    Я имею ввиду если ссылок на объект больше других нет, почему бы не изменить непосредственно сам объект, а не создавать новый? – TigerTV.ru Jan 03 '21 at 10:55
  • 1
    @TigerTV.ru Хм, этот вопрос видимо надо задать разработчикам питона. Понятно что сложный объект, у которого конструктор еще производит какие нибудь побочные эффекты сложно просто модифицировать. Но для скалярных типов можно было бы конечно исключение сделать. Но видимо решили не заморачиваться, что за объект, можно ли просто заменить, а вообще тот ли тип у нового объекта. И сделать все под одну гребенку. Скорости это конечно не добавляет... – Mike Jan 03 '21 at 11:07
  • @TigerTV.ru Сейчас решил проверить почему маленькие константы все таки всегда дают один объект. Оказалось что все целые значения меньшие либо равные (что странно) 256 всегда используют один и тот же объект. Видимо у них это какие то встроенные константные объекты. Которые они еще и ищут по значению когда присваивают – Mike Jan 03 '21 at 11:17
  • @TigerTV.ru Мне кажется я понял в чем собака зарыта. Логики кроме проверки на кол-во ссылок, тип объекта и т.п. гораздо больше понадобится. Операция a=a+1 очевидно могла бы модифицировать объект на который ссылается a. но вот для b=a+1 объект уже править нельзя. А значит оператор + когда будет выполнять сложение должен знать куда пойдет его результат. А это потребует от транслятора/интерпретатора поддерживать контекст, что его может очень усложнить. Либо делать разное поведение для a=a+1 и a+=1 – Mike Jan 03 '21 at 11:49
  • @ Mike, да интересно. Тоже попробывал print(id(1+1)) print(id(2)) print(id(256+1)) print(id(257)) . В общем нужно больше знать о внутренней структуре, чтобы можно было оптимизировать. – TigerTV.ru Jan 03 '21 at 13:20
  • @ Mike, Что интересно для отрицательных чисел тоже есть константы, но там граница только до -5. print(id(1-6)) print(id(-5)) print(id(1-7)) print(id(-6)) – TigerTV.ru Jan 03 '21 at 13:32
  • @TigerTV.ru это оптимизация, см. ответы к этому вопросу: https://ru.stackoverflow.com/q/662398 – insolor Jan 11 '21 at 09:07