Functions Try Except

Hi. I am a bit confused on why the below code will produce a result of 3?
Thank you.

m = 0
def foo(n):
    global m
    assert m==0
    try:
        return 1/n
    except ArithmeticError:
        m+=1
        raise

try:
    foo(0)
except ArithmeticError:
    m+=2
except:
    m+=1
print(m)

Hello,

please reference the code. It is basically your code with added print statements and commentary.
Because you’re dividing by zero, it raises exceptions along its executable path.

Adding printing statements to your test scripts really helps in understanding what is going on with your
code. Note that IDLE also has a debug tool that allows you to step through the code and monitor variables.

m = 0

def foo(n):
    
    global m
    assert m == 0
    
    try:
        return 1/n # Divide by zero raises an exception
    
    except ArithmeticError:
        print('entered exception 0')
        m += 1 # Was '0', now is '1' after incrementing
        raise   # Manually raising an exception here, causes it to go into the other exception outside
                # If you comment it out, it will not go into the other exception

try:
    foo(0)
except ArithmeticError: # Entered here due to the 'raise' inside the function call
    print('entered exception 1')
    m += 2  # Was '1', now is '3' after incrementing by 2
except:   # This exception is skipped because already caught by the previous exception due to arithmetic error of dividing by '0'
    print('entered exception 2')  
    m += 1
print(m)

You can remove the try/except in the function if you already have it outside
if you’d like.

1 Like

Overall, your explanation is largely correct, with a few additional points of clarification:

It’s important to point out here that while perhaps necessary for illustrating whatever point is being made by this presumably demonstration code, (non-constant) global variables should almost always be avoided unless absolutely necessary (which they rarely ever are, at least with properly-designed code), especially by beginners, since they make errors much easier to make and harder to find and solve, and the logic much harder to follow and reason about, as is indeed the case here. m gets modified several different places in several different scopes, making it substantially harder to determine why it has the particular value it does at a given point in your program’s execution.

Just to clarify, a bare raise inside an except block re-raises the existing exception, rather than raising a new exception as the comment might be read to imply—this is the same exception, not an “other exception”.

Also, its important to note that both the OP and the explanation seem to be confusing an exception, the thing that is raised, with the except blocks (called catch blocks in many other languages) that handle them, which are of course very different things. In fact, this re-raised exception is then caught by the outer except block that wraps the call to foo().

Again, to clarify, this except block is skipped (at least under normal circumstances) in the above because the ArithmeticError exception raised by the code is caught by the previous except block.

It’s also important to note that a bare except block is almost never what you want, as that will catch not only Exception subclasses but also other BaseException subclasses that you usually don’t want to catch, like KeyboardInterrupt and SystemExit, that are intended to exit your program—so if you had a bare except inside a main while loop, for instance, it could easily become to exit your program by normal means, e.g. Ctrl-C or sys.exit() and it could hang forever until the process was manually hard-killed. Instead, you almost always want at least except Exception instead, unless you know exactly what you’re doing.

1 Like

Thank you for the thorough explanations!

Thank you for the clarifications and pointers!