Почему существуют два np.int64s в numpy.core.numeric._typelessdata (почему numpy.int64 не numpy.int64?)

Это не такая уж большая проблема, как любопытство.

В моем интерпретаторе на 64-битном Linux я могу выполнить

In [10]: np.int64 == np.int64
Out[10]: True

In [11]: np.int64 is np.int64
Out[11]: True

Великолепно, что бы я ожидал. Однако я нашел это странное свойство numpy.core.numeric module

In [19]: from numpy.core.numeric import _typelessdata

In [20]: _typelessdata
Out[20]: [numpy.int64, numpy.float64, numpy.complex128, numpy.int64]

Странно, почему numpy.int64 там дважды? Давайте исследуем.

In [23]: _typelessdata[0] is _typelessdata[-1]
Out[23]: False
In [24]: _typelessdata[0] == _typelessdata[-1]
Out[24]: False
In [25]: id(_typelessdata[-1])
Out[25]: 139990931572128
In [26]: id(_typelessdata[0])
Out[26]: 139990931572544
In [27]: _typelessdata[-1]
Out[27]: numpy.int64
In [28]: _typelessdata[0]
Out[28]: numpy.int64

О, они разные. Что здесь происходит? Почему существуют два np.int64?

Ответ 1

Здесь - строки, в которых _typelessdata построено в пределах numeric.py:

_typelessdata = [int_, float_, complex_]
if issubclass(intc, int):
    _typelessdata.append(intc)

if issubclass(longlong, int):
    _typelessdata.append(longlong)

intc - C-совместимое (32-битное) целое число со знаком, а int - родной Python целое число, которое может быть либо 32-битным, либо 64-битным в зависимости от платформы.

  • В 32-битной системе собственный тип Python int также 32 бит, поэтому issubclass(intc, int) возвращает True и intc добавляется к _typelessdata, который заканчивается следующим образом:

    [numpy.int32, numpy.float64, numpy.complex128, numpy.int32]
    

    Обратите внимание, что _typelessdata[-1] is numpy.intc, а не numpy.int32.

  • В 64-битной системе int составляет 64 бит, поэтому issubclass(longlong, int) возвращает True, а longlong добавляется к _typelessdata, в результате чего:

    [numpy.int64, numpy.float64, numpy.complex128, numpy.int64]
    

    В этом случае, как указал Джо, (_typelessdata[-1] is numpy.longlong) == True.


Чем больше вопрос, почему содержимое _typelessdata установлено так. Единственное место, которое я мог найти в источнике numpy, где _typelessdata фактически используется эта строка в определении для np.array_repr в том же файле:

skipdtype = (arr.dtype.type in _typelessdata) and arr.size > 0

Цель _typelessdata состоит в том, чтобы гарантировать, что np.array_repr правильно печатает строковое представление массивов, чей dtype оказывается таким же, как и собственный (на основе платформы) собственный целочисленный тип Python.

Например, в 32-битной системе, где int равно 32 бит:

In [1]: np.array_repr(np.intc([1]))
Out[1]: 'array([1])'

In [2]: np.array_repr(np.longlong([1]))
Out[2]: 'array([1], dtype=int64)'

тогда как на 64-битной системе, где int равно 64 бит:

In [1]: np.array_repr(np.intc([1]))
Out[1]: 'array([1], dtype=int32)'

In [2]: np.array_repr(np.longlong([1]))
Out[2]: 'array([1])'

Проверка arr.dtype.type in _typelessdata в приведенной выше строке гарантирует, что печать dtype будет пропущена для соответствующего зависимого от платформы нативного целого dtypes.

Ответ 2

Я не знаю всей истории за ней, но второй int64 на самом деле numpy.longlong.

In [1]: import numpy as np

In [2]: from numpy.core.numeric import _typelessdata

In [3]: _typelessdata
Out[4]: [numpy.int64, numpy.float64, numpy.complex128, numpy.int64]

In [5]: id(_typelessdata[-1]) == id(np.longlong)
Out[5]: True

numpy.longlong предполагается непосредственно соответствует типу C long long. C long long определяется как минимум 64 бита в ширину, но точное определение остается до компилятора.

Моя догадка заключается в том, что numpy.longlong завершает работу в качестве другого экземпляра numpy.int64 для большинства систем, но может быть чем-то другим, если C complier определяет long long как нечто большее, чем 64 бит.