Перехват OverflowError - PullRequest
0 голосов
/ 16 мая 2018

В Python 3 у меня есть класс, представляющий диапазон значений [x, y] и вычисляющий длину такого диапазона.

Если длина слишком велика, я не уверен, как пойматьИсключение OverflowError внутри самого класса.Он вызывается только во внешнем коде с использованием экземпляра класса ...

class ValRange:
    val_min = None
    val_max = None
    range_len = 0

    def __init__(self, val_min, val_max):
        self.val_min = val_min
        self.val_max = val_max

    def __len__(self):
        if (self.val_min is not None and
             self.val_max is not None):
            length = 0
            try:
                length = int(self.val_max) - int(self.val_min) + 1
            except OverflowError as e:
                # Somehow no exception is caught here...
                print('OverflowError...')
                length = 10**5  # arbitrarily large number
            except Exception as e:
                print(e)
            if length > 0:
                self.range_len = length
        return self.range_len


import traceback
import uuid
x = uuid.UUID('00000000-cb9d-4a99-994d-53a499f260b3')
y = uuid.UUID('ea205f99-0564-4aa0-84c3-1b99fcd679fd')
r = ValRange(x, y)
try:
    print(len(r))
except:
    # The exception is caught in this outer code and not in the class itself. Why?
    print(traceback.format_exc())

# The following, which is equivalent to the operations in the 
# code above, will work. 
a = int(y) - int(x) + 1
print(a)

Вот что происходит при выполнении:

Traceback (most recent call last):
  File "/home/rira/bugs/overflow.py", line 35, in <module>
    print(len(r))
OverflowError: cannot fit 'int' into an index-sized integer

311207443402617699746040548788952897867

1 Ответ

0 голосов
/ 16 мая 2018

Это потому, что OverflowError не встречается в вашем волшебном методе __len__() - Python вполне способен обрабатывать намного большие целые числа, чем это - но в CPython len() сам реализован как PyObject_Size(), который возвращает Py_ssize_t, который ограничен 2^31-1 (32-разрядный) или 2^63-1 (64-разрядный), и, таким образом, переполнение происходит, когда ваш результат __len__() равен принудительно к нему.

Вы можете выполнить предварительную проверку перед возвратом результата, чтобы убедиться, что вы зафиксировали переполнение еще до того, как оно произойдет, что-то вроде:

def __len__(self):
    if (self.val_min is not None and self.val_max is not None):
        length = int(self.val_max) - int(self.val_min) + 1
        if length > sys.maxsize:
            print('OverflowError...')
            length = 10**5  # arbitrarily large number
        self.range_len = length
    return self.range_len
...