关于错误处理:在Python中完全禁用/替换ZeroDivisionError

Completely disabling / substituting ZeroDivisionError in Python

很好的一天。我一直在搜索相关的帖子,但没有找到理想的解决方案。让我描述一下我的问题:

我正在分析语料库中的文本,并从这些文本中提取特征,然后将这些特征存储在一个数组中。其中一些特征包括男性代词"he"与女性代词"she"的比例。问题是,对于某些变量,值将为零,并且它们将引起零分割误差。

因为我计算了大约100个这些比率,所以在每个比率计算周围包装一个try/catch异常听起来太麻烦了。

我发现我能做到

1
2
#16,RATIO_masculine_femenine
feature_map.append(numOfHe / numOfShe if numOfShe else 0)

但这还是有点太费劲了。我想知道在脚本的开头是否有一种方法可以说明任何零分区错误都应该由NaN或0替换,或者任何其他合适的值。

谢谢


如果将int对象包装在自定义子类中,则可以对其进行一次寻址:

1
2
3
4
5
6
class SafeInt(int):
    def __div__(self, y):
        try:
            return SafeInt(super(SafeInt, self).__div__(y))
        except ZeroDivisionError:
            return SafeInt(0)

覆盖所有ints:

1
2
3
4
original_int = int
int = SafeInt
int(5) / 0
# O: 0

凌驾于某些ints之上:

1
2
SafeInt(5) / 0
# O: 0

不过,你要小心保持物体的安全。你会注意到我在__div__里返回的所有东西都包在SafeInt()里。int对象是不可变的,每次都必须显式返回一个新的SafeInt对象。这意味着您可能需要制作一个装饰器,使SafeInt()中的每个函数都能确保这一点。我把它留给读者作为练习!

否则,您将得到以下结果:

1
2
3
4
5
6
7
8
9
10
>>> SafeInt(5) / 0
0   # this is a SafeInt object
>>> _ / 0
0   # this is a SafeInt object; no error
>>> SafeInt(5) + 0
5   # this is a basic int object
>>> _ / 0
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

最后一点:你可以把SafeInt作为defaultdict的论点传给SafeInt的所有成员!

编辑:我知道你希望这件事发生在所有的int上,我希望这样的事情能奏效,但这是不允许的(出于正当理由):

1
2
3
4
5
6
7
8
9
10
11
12
>>> def wrapdiv(olddiv):
...     def newdiv(self, y):
...         try:
...             olddiv(self, y)
...         except ZeroDivisionError:
...             return 0
...     return newdiv
...
>>> int.__div__ = wrapdiv(int.__div__)
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'int'


pythonic的答案是将其包装成一个函数,例如:

1
2
3
4
5
6
7
def ratio(a, b):
    if b == 0:
        return 0
    else:
        return a / b

feature_map.append(ratio(numOfHe, numOfShe))

函数的确切形式取决于代码的其余部分,但是如果您编写了这样的一行代码数百次,那么您可能应该将它包装在函数中,或者至少使用循环。另外,像numOfHenumOfShe这样的变量名暗示,听写可能会更好地为您服务。

更新

我从您的代码链接中看到,每个计算实际上是完全不同的,所以您可能无法轻松地循环它。由于计算仍然相对简单,您可以尝试使用eval这样的技巧:

1
2
3
4
5
6
7
8
9
10
11
12
13
calcs = [
    ...
    (12, 'h + ha + hw + hy'),
    (13, '(h + ha) / (hw + hy)'),
    ...
]

for index, calc in calcs:
    try:
        v = eval(calc, locals())
    except ZeroDivisionError:
        v = 0
    feature_map.append(v)

您还可以将其他信息添加到calcs中,并使用一个namedtuple来代替。如果这有帮助的话,您也可以根据需要使用类来动态地评估计算。