python - 为什么异常/错误在 python 中评估为 True?

标签 python exception

在几个地方我必须从字典中检索一些值,但需要检查该值的键是否存在,如果不存在我使用一些默认值:

    if self.data and self.data.has_key('key'):
        value = self.data['key']
    else:
        value = self.default
    ....

我喜欢 python 的一件事是和/或 bool 运算符返回它们的一个操作数。我不确定,但如果异常被评估为假,上面的代码可以重写如下:

    value = self.data['key'] or self.default

我认为错误应该评估为 false(就像在 bash 编程中一样)是很直观的。 python 将异常视为 true 有什么特殊原因吗?

编辑:

我只是想表达我对“异常处理”主题的看法。来自 wikipedia :

" 从例程作者的角度来看,引发异常是发出例程无法正常执行信号的有用方法。例如,当输入参数无效时(例如除法中的零分母)或当它依赖的资源不可用时(例如丢失文件或硬盘错误)。在没有异常的系统中,例程需要返回一些特殊的错误代码。然而,这有时会因半谓词问题而变得复杂,其中例程的用户需要编写额外的代码来区分正常的返回值和错误的返回值。 "

正如我所说,引发异常只是从函数返回的一种奇特方式。 “异常对象”只是指向包含有关错误信息的数据结构的指针,以及如何在高级语言上处理它仅取决于 VM 的实现。我决定通过查看“dis”模块的输出来了解 python 2.6.6 如何处理异常:

>>> import dis
>>> def raise_exception():
...     raise Exception('some error')
... 
>>> dis.dis(raise_exception)
  2           0 LOAD_GLOBAL              0 (Exception)
              3 LOAD_CONST               1 ('some error')
              6 CALL_FUNCTION            1
              9 RAISE_VARARGS            1
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE        

很明显,python 在字节码级别具有异常概念,但即使没有,它仍然可以使异常处理构造像它们那样运行。采取以下功能:

def raise_exception():
...     return Exception('some error')
... 
>>> dis.dis(raise_exception)
  2           0 LOAD_GLOBAL              0 (Exception)
              3 LOAD_CONST               1 ('some error')
              6 CALL_FUNCTION            1
              9 RETURN_VALUE

这两个函数都以与 0、3 和 6 中所示相同的方式创建异常对象。区别在于第一个调用对象上的 RAISE_VARARGS 指令(并且仍然返回 None),第二个将只返回异常对象调用代码。现在采用以下字节码(我不确定这是否正确):

0  LOAD_GLOBAL      (isinstance) #Loads the function 'isinstance'
3  LOAD_GLOBAL      (raise_exception) #loads the function 'raise_exception'
6  CALL_FUNCTION    #call the function raise_exception(which will push an Exception instance to the stack
9  LOAD_GLOBAL      (Exception) #load the Exception class
12 CALL_FUNCTION    #call the 'isinstance function'(which will push 'True to the stack)
15 JUMP_IF_TRUE     (to 27) #Will jump to instruction 33 if the object at the top of stack evaluates to true
18 LOAD_CONS        ('No exceptions were raised')
21 PRINT_ITEM
24 PRINT_NEWLINE    
27 LOAD_CONS        ('Exception caught!')
21 PRINT_ITEM
24 PRINT_NEWLINE

上面的翻译成等价的东西:

if isinstance(raise_exception(), Exception):
    print 'Exception caught!'
else:
    print 'No exceptions were raised'

但是,编译器在找到 try block 时可能会生成类似上述指令的内容。通过此实现,某人可以测试 block 中的异常或将返回异常的函数视为“False”值。

最佳答案

您误解了异常的使用。一个异常(exception)是出了点问题。它不仅仅是一个返回值,也不应该被这样对待。

Explicit is better than implicit.

因为出现问题时会引发异常,所以您必须显式编写代码来捕获它们。这是故意的——您不应该仅仅将异常视为 False,因为它们不止于此。

如果您像您建议的那样吞下了异常,您将无法判断代码何时出错。如果你引用一个未绑定(bind)的变量会发生什么?那应该给你一个NameError,但它会给你... False?!


考虑你的代码块:

value = self.data['key'] or self.default

如果 key 不是 self.data 的键,您希望 self.data['key'] 返回 False 。您认为这种方法有什么问题吗?如果 self.data == {'key': False} 会怎样?您无法区分存储在字典中的 False 和由于缺少键而返回的 False 的情况。

此外,如果这被更普遍地改变,以便所有异常被吞没(!),你将如何区分KeyError('key' not in self.data) 并说出 NameError(self.data not defined)?两者都将评估为 False

显式编写代码来捕获异常可以解决这个问题,因为您可以准确地捕获您想要的那些异常:

try:
    value = self.data['key']
except KeyError:
    value = self.default

不过,已经编写了一个数据结构来为您执行此操作,因为通常需要默认字典。它在集合中:

>>> import collections
>>> data = collections.defaultdict(lambda: False)
>>> data['foo'] = True
>>> data['foo']
True
>>> data['bar']
False

关于python - 为什么异常/错误在 python 中评估为 True?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3768438/

相关文章:

ios - 使用我自己的广告单元 ID 时 AdMob 崩溃

c++ - __try/__except block 或 try/catch block 哪个更好用?

java - 在 Java 中,什么时候应该创建检查异常,什么时候应该创建运行时异常?

linux - 为什么异常处理程序要查找系统调用表?

Python,针对频繁模式的网络日志数据挖掘

python - 使用 python 和 Kerberos 连接到 Hive

python - 在 config.py 中导入值

c# - 异常的 "message"是否具有文化独立性?

python - 如何让 Bokeh 的 'PointDrawTool' 与 NetworkX Spring_Layout 一起使用

python - 如何将python字典转换为pandas中的数据框