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

Thi*_*uda 2 python exception-handling

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

    if self.data and self.data.has_key('key'):
        value = self.data['key']
    else:
        value = self.default
    ....
Run Code Online (Sandbox Code Playgroud)

我喜欢python的一件事是,和/或布尔运算符返回其中一个操作数.我不确定,但如果异常评估为false,则上述代码可以重写如下:

    value = self.data['key'] or self.default
Run Code Online (Sandbox Code Playgroud)

我认为直观的错误应该评估为false(比如在bash编程中).python将异常视为真是否有任何特殊原因?

编辑:

我只想表明我对"异常处理"主题的看法.来自维基百科:

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

正如我所说,提出异常只是从函数返回的一种奇特方式."异常对象"只是指向数据结构的指针,该数据结构包含有关错误的信息以及如何在高级语言上处理错误仅取决于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        
Run Code Online (Sandbox Code Playgroud)

很明显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
Run Code Online (Sandbox Code Playgroud)

这两个函数都以与0,3和6中显示相同的方式创建异常对象.不同之处在于,第一个调用对象上的RAISE_VARARGS指令(并且Stills返回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
Run Code Online (Sandbox Code Playgroud)

以上翻译成与此相当的东西:

if isinstance(raise_exception(), Exception):
    print 'Exception caught!'
else:
    print 'No exceptions were raised'
Run Code Online (Sandbox Code Playgroud)

但是,编译器在找到try块时可能会生成类似上述指令的内容.通过此实现,有人可以测试块的异常,也可以将返回异常的函数视为"False"值.

Kat*_*iel 9

您误解了异常的使用.一个例外是出了问题.它不仅仅是一个返回值,也不应该这样对待.

显式优于隐式.

因为出现错误时会引发异常,所以必须显式编写代码来捕获它们.这是故意的 - 你不应该仅仅忽略异常False,因为它们不止于此.

如果您按照您的建议吞下异常,则无法判断代码何时出错.如果您引用了未绑定的变量会发生什么?这应该给你一个NameError,但它会给你...... False?!


考虑一下你的代码块:

value = self.data['key'] or self.default
Run Code Online (Sandbox Code Playgroud)

如果不是关键,你想要self.data['key']返回.你觉得这种方法有什么问题吗?怎么办?您无法区分存储在字典中的情况和由于缺少密钥而返回的情况.Falsekeyself.dataself.data == {'key': False}FalseFalse

此外,如果这是更普遍的改变,使所有的异常被吞噬(!),你会如何区分一个KeyError('key' not in self.data),并说NameError(self.data not defined)?两者都会评估False.

显式编写代码以捕获异常可以解决这个问题,因为您可以准确捕获所需的异常:

try:
    value = self.data['key']
except KeyError:
    value = self.default
Run Code Online (Sandbox Code Playgroud)

但是,已经编写了一个可以为您执行此操作的数据结构,因为通常需要使用默认字典.它在collections:

>>> import collections
>>> data = collections.defaultdict(lambda: False)
>>> data['foo'] = True
>>> data['foo']
True
>>> data['bar']
False
Run Code Online (Sandbox Code Playgroud)


Man*_*dan 5

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

Exception评估实例True(编辑请参阅下面的@ THC4k评论.相关信息).这并不妨碍他们被抛出.

我不确定,但如果异常评估为假

在您的上下文中,Exceptions评估为止是不够的False.它们也不应该被抛出并沿着调用堆栈传播.相反,他们必须在现场"停止"然后评估False.

我会留给更有经验的Pythonistas来评论为什么例外不会(或不应该)被"停止"并评估为TrueFalse.我猜这是因为它们会被抛出并传播.事实上,如果他们被停止和审讯= P,他们将不再是"例外".

但是需要检查该值的密钥是否存在,如果不存在,我会使用一些默认值

在没有字典中的键的情况下,我可以想到两种方法来获取默认值.一个是使用defaultdict.另一种是使用该get方法.

>>> from collections import defaultdict
>>> d = defaultdict(lambda: "default")
>>> d['key']
'default'

>>> d = dict()
>>> d.get('key', 'default')
'default'
>>> 
Run Code Online (Sandbox Code Playgroud)

PS:if key in dict比较优先dict.has_key(key).实际上has_key()已经在Python 3.0中删除了.

  • 你说"*Instance*of Exception评估为'True`"但是`Exception`本身也评估为'True`(类和函数也是如此).实际上,*everything*默认评估为"True".只有少数内置函数会覆盖它,例如空容器(`(),[],{}`)和零(`0,0.0,0j,False`). (3认同)