Python 2.7 - finally子句上的奇怪行为

时间:2016-06-13 07:25:20

标签: python python-2.7 exception-handling raspberry-pi

我遇到了一个非常奇怪的问题,这让我想知道我是否理解异常处理。

我有一个代码(我将在最后发布)看起来或多或少像这样:

try:
    doSomething()
finally:
    print 'bye'

当我通过ctrl + c退出程序时,finally子句中的代码没有被执行。

更糟糕的是,现在考虑以下事项:

try:
    doSomething()
except:  # this could be replaced by except Exception, it doesn't matter
    print 'something'
finally:
    print 'bye'

现在不执行except子句中的代码..但finally子句中的代码是!

我意识到这必须是doSomething()执行的代码的错误。但我的问题是,它怎么可能呢?我认为我们可以100%确信最终的条款总是被执行。

这是真正的代码。它运行在树莓派3上。它是对here找到的代码的改编。

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

# Define function to measure charge time
def RCtime (PiPin):
    # Discharge capacitor
    GPIO.setup(PiPin, GPIO.OUT)
    GPIO.output(PiPin, GPIO.LOW)
    time.sleep(.1)

    time1 = time.time()
    GPIO.setup(PiPin, GPIO.IN)
    if (GPIO.input(PiPin) == GPIO.LOW):
        GPIO.wait_for_edge(PiPin, GPIO.RISING, timeout=1000)
    time_elap = time.time()-time1

    return time_elap*1e3

# Main program loop
try:
    while True:
        print RCtime(4) # Measure timing using GPIO4
except Exception:
    print '---------got ya-----------------'
finally:
    print '---Finaly---'
    GPIO.cleanup() # this ensures a clean exit  

更具体地说,当程序在GPIO.wait_for_edge(PiPin, GPIO.RISING, timeout=1000)行等待时,会出现所描述的行为。

2 个答案:

答案 0 :(得分:0)

如果您的代码不处理KeyboardInterrupt异常,系统必须这样做:这意味着它会杀死代码。相反,如果你有一个异常处理程序,你的代码将接管,离开循环,然后执行finally块。

答案 1 :(得分:0)

嗯,这似乎是与RPi.GPIO处理信号的方式有关的错误(例如KeyboardInterrupt)。该模块调用用c编写的一些函数。该函数有点捕获KeyboardInterrupt并抛出异常,但它没有正确执行。两个例外不是只有一个例外,而是“堆叠”。因此,我的代码中的异常之后运行的第一件事将由第二个异常终止。如果我不包含except块,则finally块在第一个异常之后执行,并由第二个异常终止。如果我包含一个except块,它会尝试在第一个异常之后运行,由于第二个异常而失败,然后它转到finally块。 我只找到一个人forum来处理类似的问题。

  

在我之前的实验之后,我很好奇这是如何工作的   input()[https://svn.python.org/projects/python/trunk/Parser/myreadline.c]。我用raw_input()替换了sem.acquire()并运行了同样的东西   试验。现在内部异常真的被采用,所以它像OP一样工作   预期。但是,例外是KeyboardInterrupt,而不是特殊的   IPC模块的例外情况。   所以我在源代码中查看了它们是如何做到的:   代码在Parser / myreadline.c中。

     

此函数中的输入代码调用PyErr_CheckSignals()和   PyOS_InterruptOccurred()用于正确处理中断。所以   OP似乎应该做类似的事情。 Onl; y提供自定义   错误你将不得不做一些其他的东西。我不知道是什么,但也许   调用PyErr_SetString就足够了,因为它可能会覆盖   KeyboardInterrupt东西。