2016-07-07 2 views
7

В this Ответ Я нашел способ получить ссылку на количество объектов в Python.Почему Python Ref подсчитывает малые целые числа на удивление высоко?

Они упомянули, используя sys.getrefcount(). Я попробовал, но я получаю неожиданный результат. Когда есть 1 ссылка, кажется, что счет равен 20. Почему?

Я посмотрел на documentation, но, похоже, это не объясняет причину.

enter image description here

+1

Попробуйте 'sys.getrefcount (257)', и это, вероятно, заметно снизится. –

+0

Попробуйте 'sys.getrefcount (None)' для интересного номера. – cdarke

+1

Это действительно странно, я никогда не создавал var с 257. Почему он возвращает 3? –

ответ

5

Этот объект происходит иметь 20 ссылок на него в момент первого sys.getrefcount вызова. Это не просто ссылки, которые вы создали; есть всевозможные другие ссылки на него в других модулях и внутри внутренних компонентов Python, поскольку (это деталь реализации) стандартная реализация Python создает только один объект 100 и использует его для всех вхождений 100 в программу Python.

+0

Интересно, приятно знать, спасибо! –

+1

Более того, если вы создадите свои собственные объекты (списки, кортежи, экземпляры классов), вы получите ожидаемое поведение. – alexis

5

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

  • Python пытается сохранить только одно фактическое значение для каждой ссылки. Таким образом, 100 в вашем примере будут равны 100, что является некоторым внутренним пределом для вызовов рекурсии или того же 100, что и индекс текущего цикла.
  • Python сохраняет дополнительные ссылки на некоторые общие объекты, включая низкие целые числа. Исходное число до 1 234 567 отличается от счета до 20.
  • Многие функции запоминают и сохраняют ссылки на недавние аргументы.
  • Некоторые переводчики сохраняют ссылки на последние значения и значения, указанные в последних строках. Например, предыдущее возвращаемое значение сохраняется в «_». Это означает, что запуск в интерпретаторе и запуск из командной строки даст разные ответы.
  • Как и все схемы подсчета ссылок, есть ошибки. Например, PyTuple_GetItem() имеет некоторые сомнительные варианты.

Точные подсчеты ссылок и их значения будут отличаться в PyPi от C-Python и IPython. Считывание ссылок редко является хорошим инструментом для поиска нечетного поведения в Python.

4

Приятно прочесть исходный код для Python2.7, который очень хорошо написан и понятен для чтения. (Я имею в виду версию 2.7.12, если вы хотите поиграть дома.) Хорошим местом для начала понимания кода является отличная серия лекций: C Python Internals, которая начинается с перспективы новичка.

Критический код (написанный на C), относящийся к нам, отображается в файле 'Objects/intobject.c' (я удалил некоторый код #ifdef и немного изменил создание нового объекта Integer для ясности):

По существу, он создает пресетный массив, содержащий все числа от -5 до 256 включительно и, если возможно, использует эти объекты (увеличивая количество ссылок на них с использованием макроса Py_INCREF). Если нет, он создаст новый объект PyInt_Type, который инициализируется с номером ссылки 1.

Тайна того, почему каждое число, похоже, имеет счетчик ссылок 3 (фактически практически любой новый объект), раскрывается только при просмотре байтового кода, который генерирует Python. Виртуальная машина работает со стеком значений (немного как в Forth), и каждый раз, когда объект помещается в стек значений, он увеличивает счетчик ссылок.

Так что я подозреваю, что ваш код сам предоставляет все 3 ссылки, которые вы видите, поскольку для чисел, не входящих в список небольших значений, вы должны получить уникальный объект. Первая ссылка, по-видимому, находится в стеке значений для вызывающего абонента getrefcount при его вызове; второй - в локальном списке переменных для кадра getrefcount; третий, вероятно, находится в стеке значений в кадре getrefcount, когда он просматривает свой счетчик ссылок.

Полезный инструмент, если вы хотите вникать в проблему, - это команда «компиляция» и команда «dis» (дизассемблировать), которая находится в модуле «dis», что вместе позволит вам прочитать фактическое байт-код, сгенерированный любым фрагментом кода Python, и должен помочь вам точно узнать, когда и где создается третья ссылка.

Что касается более высоких контрольных значений для небольших значений, то при запуске Python он автоматически загружает всю стандартную библиотеку и запускает довольно много кода инициализации модуля Python, прежде чем вы начнете интерпретировать свой собственный код. Эти модули содержат собственные копии многих небольших целых чисел (и объект None, который также уникален).