简体   繁体   中英

“raise” at the end of a python function outside “try” or “except” block

What does raise do, if it's not inside a try or except clause, but simply as the last statement in the function?

def foo(self):
    try:
        # some code that raises an exception
    except Exception as e:
        pass

    # notice that the "raise" is outside
    raise

This example prints 1 but not 2 so it must be that the last raise statement simply raises the last thrown exception.

def foo():
    try:
        raise Exception()
    except Exception as e:
        pass

    print 1
    raise
    print 2

if __name__ == '__main__':
    foo()

Any official documentation for this type of usage pattern?

As Russell said,

A bare raise statement re-raises the last caught exception.

It doesn't matter whether this is happening in a try-except block or not. If there has been a caught exception, then calling raise will re-raise that exception. Otherwise, Python will complain that the previously caught exception is None and raise a TypeError because None is not something that can actually be raised.

As tdelaney said, it doesn't seem to make sense to do this except in an error-handling function. Personally I'd say that it doesn't even belong in an error-handling function, as the raise should still be in the except clause. Someone could use this in an attempt to execute code whether or not an error occurs, but a finally clause is the proper way to do that. Another possibility would be using this as a way to determine if an error occurred while executing the function, but there are much better ways to do that (such as returning an extra value that indicates if/where an error occurred).

A Bare raise reraises the current exception. This usually makes no sense at the end of a function, unless the function is called in an exception:

By itself, the raise is invalid and python throws its own exception

>>> def x():
...     raise
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in x
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType

But if called within an exception block, it acts sanely

>>> try:
...     int('a')
... except:
...     x()
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'a'
>>> 

EDIT

This might be a perfectly reasonable thing to do if the function is attempting some sort of recovery. The function could fix what's broken, log a message, trigger the fire extinguishers, etc... and raise if it still thinks the system is in error.

A bare raise statement re-raises the last caught exception. https://docs.python.org/2/tutorial/errors.html#raising-exceptions

From this documentation we can read:

If no expressions are present, raise re-raises the last exception that was active in the current scope. If no exception is active in the current scope, a TypeError exception is raised indicating that this is an error (if running under IDLE, a Queue.Empty exception is raised instead).

This means that, in the case of your code, if no exception occurs within the try ... except block, then you are forcing the program to raise a TypeError exception to happen.

I had a problem like this where I needed to raise a previously caught exception outside the try/except block if my function didn't return a value. I did a bit of looking around in the sys and traceback modules, but couldn't find a good method to do this, so I just ended up storing the exception outside the block.

def foo():
    caught = None

    try:
        raise Exception
    except Exception as e:
        caught = e
        pass

    raise caught


f = foo()

Output

Traceback (most recent call last):
  line 13, in <module>
  line 10, in foo
  line 5, in foo
Exception

Clearly this isn't useful in the above example, but it's pretty useful if you need to try something quite a few times in a loop and re-raise. My specific need was for an HTTP request retry mechanism.

import time

def foo(key):
    caught = None
    
    for i in [1, 2, 3, 4, 5]:
        try:
            return d[key]
        except KeyError as e:
            caught = e
            print(i)
            time.sleep(i)
            continue

    raise caught

d = {"bar": "baz"}

f = foo(key="baz")

Output

1
2
3
4
5
Traceback (most recent call last):
  line 19, in <module>
  line 15, in foo
  line 8, in foo
KeyError: 'baz'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM