【问题标题】:Completely disabling / substituting ZeroDivisionError in Python在 Python 中完全禁用/替换 ZeroDivisionError
【发布时间】:2014-01-19 14:50:19
【问题描述】:

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

我正在分析语料库中的文本,并从这些文本中提取特征,然后将这些特征存储在一个数组中。其中一些特征涉及比例,例如男性代词“他”与女性代词“她”的比例。问题是,对于某些变量,值将为零,它们将引发 ZeroDivisionError。

由于我计算了大约 100 个这些比率,因此在每个比率计算周围都包含一个 try/catch 异常听起来太麻烦了。

我发现我可以做到

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

但这还是有点太费力了。 我想知道是否有办法在脚本的开头声明任何 ZeroDivisionError 都应该用 NaN 或 0 或任何其他可能适合的值代替。

谢谢

【问题讨论】:

  • 你看过最上面的答案here吗?

标签: python error-handling divide-by-zero


【解决方案1】:

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

def ratio(a, b):
    if b == 0:
        return 0
    else:
        return a / b

feature_map.append(ratio(numOfHe, numOfShe))

函数的确切形式取决于您的其余代码,但如果您要编写数百次这样的行,那么您可能应该将它包装在一个函数中,或者至少使用一个循环。此外,numOfHenumOfShe 之类的变量名称暗示您可能更适合使用 dict。

更新

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

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。如果有帮助的话,您也可以使用类来动态评估计算。

【讨论】:

  • 如果您的数据结构或多或少是全局的,您可以从 ratio 函数中引用字典,只需提供键作为参数,例如 ratio('he', 'she')
  • 谢谢,看来我确实要创建一个“比率”函数,但我觉得很懒……“/”太方便了:) 我认为没有办法马上那么禁用/替换内置错误呢?至于变量,我认为其他东西可能会更好,(某种 numpy 数组?)但不是 dict,我计划通过索引号访问 feature_map,并且还有一个逻辑索引来告诉哪些功能是活跃的一种学习算法
  • 其实我有一个collections.Counter作为单词列表,我计算比率为ratio = Counter[he] / Counter[she]。特征 _map 用于存储实际结果。但是是的,我认为这可以更好地实现,问题是我没有时间:(
  • 最好说没有sane方法可以全局替换错误。你可能会用一个聪明的上下文处理程序来管理一些东西并做框架魔术,但我真的不推荐它。最好的方法是以不重复代码 100 次的方式编写代码!编写一个 5 行函数真的需要那么长时间吗?有许多结构可用于存储数据。最好的取决于您的使用情况。
  • 比率函数是一个很好的解决方案,非常感谢,现在,关于写 200 次 list.append(ratio(x,y)),我不明白我该怎么做另一种方式。我要对每个文本进行 200 多次计算,这意味着不同的比率、单词的频率和总和等。对于那些我必须做一个数据向量(feature_map),但它们需要按顺序排列,以便下一个文本有具有自己的值的相同功能。我通过按顺序计算每个度量,然后为每个计算附加它来做到这一点。也许我可以使用函数字典?但这如何保持订单?
【解决方案2】:

如果您将 int 对象包装在自定义子类中,则可以处理一次:

class SafeInt(int):
    def __div__(self, y):
        try:
            return SafeInt(super(SafeInt, self).__div__(y))
        except ZeroDivisionError:
            return SafeInt(0)

覆盖所有 ints:

original_int = int
int = SafeInt
int(5) / 0
# O: 0

覆盖一些 ints:

SafeInt(5) / 0
# O: 0

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

否则你会得到这样的结果:

>>> 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


编辑:知道你希望它发生在 all ints,我希望这样的事情可能会起作用,但它是不允许的(有充分的理由):

>>> 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'

【讨论】:

  • 感谢您的详尽回答。有点着急(必须在周二给出一些结果)我现在不会实现它,但以后一定会研究它,谢谢!
猜你喜欢
  • 2013-03-02
  • 1970-01-01
  • 2016-05-16
  • 2015-10-17
  • 2023-03-19
  • 1970-01-01
  • 2015-02-23
  • 2018-01-29
  • 2015-08-24
相关资源
最近更新 更多