捕获溢出错误

Catching OverflowError

在 Python 3 中,我有一个 class 表示值的范围 [x,y] 并计算该范围的长度。

如果长度太大,我不确定如何在 class 本身 中捕获 OverflowError 异常。它仅在使用 class 实例的外部代码中引发...

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

那是因为 OverflowError 不会出现在你神奇的 __len__() 方法中 - Python 完全有能力处理比它大得多的整数 - 但在 CPython len() 本身实现为 PyObject_Size() which returns a 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