В этой задаче я имею дело с адресными пространствами сети IPv6, поэтому длина равна 2^(128-subnet).

Похоже, что python (по крайней мере, на этой машине) справится с 64-битным числом со знаком в качестве возвращаемого значения из __len__(). Итак, len(IP('2001::/66')) работает, но len(IP('2001::/65')) не работает.

from IPy import IP
len(IP('2001::/64'))
Traceback (most recent call last):
  File "test.py", line 2, in <module>
      len(IP('2001::/64'))
OverflowError: long int too large to convert to int

Рассматриваемая библиотека IPy находится по адресу https://github.com/haypo/python-ipy.

Любые предложения о том, как с этим справиться, или намек на то, что это может быть ограничение, с которым я застрял?

13
Jeff Ferland 27 Мар 2013 в 06:20
+1 потому что мне это интересно. В x64 Win7 при использовании Python 2.6 len(IP('2001::/64')) дает мне TypeError: __len__() should return an int, тогда как IP('2001::/64').__len__() дает мне 18446744073709551616L. Что вам дает IP('2001::/64').__len__()?
 – 
azhrei
27 Мар 2013 в 06:31
OverflowError вместо TypeError в 2.7.3, но тот же шаблон с .__len__(), работающим нормально, и len(), вызывающим исключение.
 – 
Jeff Ferland
27 Мар 2013 в 06:34
Что ж, комментарии в исходном коде IPy предполагают, что это ограничение Python, с которым вы застряли. bugs.python.org/issue3729, похоже, сообщает об основной проблеме и о том, что она была обнаружена в версиях 2.6 и 3.0. Ошибка закрыта как исправленная, но я не вижу, где она была исправлена. Может, стоит попробовать Python 3.3?
 – 
azhrei
27 Мар 2013 в 07:08
Python 3.2.3 дает мне OverflowError: cannot fit 'int' into an index-sized integer. Это может оказаться просто примечанием к документации для использования x.len(), а не len(x).
 – 
Jeff Ferland
27 Мар 2013 в 07:11

1 ответ

Лучший ответ

Проблема, с которой вы сталкиваетесь, заключается в том, что C API Python имеет системно-зависимое ограничение на длину контейнеров. То есть функция C PyObject_Size возвращает значение Py_ssize_t, которое является подписанной версией стандартного типа C size_t. Его размер зависит от системы, но, вероятно, 32-битный в 32-битных системах и 64-битный в 64-битных системах.

Встроенная функция len использует PyObject_Size, поэтому имеет те же ограничения. Вот его текущая реализация:

static PyObject *
builtin_len(PyObject *self, PyObject *v)
{
    Py_ssize_t res;

    res = PyObject_Size(v);
    if (res < 0 && PyErr_Occurred())
        return NULL;
    return PyInt_FromSsize_t(res);
}

Это ограничение можно обойти, используя метод len объекта IP вместо вызова встроенной функции len:

IP('2001::/64').len()

Это чистый питон, поэтому у него нет ограничений на целочисленный размер.

13
Blckknght 27 Мар 2013 в 07:12
Прохладный. Я внесу это в документацию IPy.
 – 
Jeff Ferland
27 Мар 2013 в 07:19
Стоит ли сообщать об этом как об ошибке? Нет причин, по которым длины и индексы не могут превышать этот размер, особенно когда у нас есть «виртуальные» наборы, подобные этому.
 – 
Lambda Fairy
27 Мар 2013 в 08:43
Я запутался. Сообщаете кому как об ошибке?
 – 
Jeff Ferland
27 Мар 2013 в 08:56
1
Сообщаем об этом разработчикам Python. Согласно официальной документации, __len__ должен вернуть «целое число». Очень большое целое число по-прежнему остается целым.
 – 
Lambda Fairy
27 Мар 2013 в 11:12