Simple: while does not create a scope in Python. Python has only the following scopes:

  • function scope (may include closure variables)
  • class scope (only while the class is being defined)
  • global (module) scope
  • comprehension/generator expression scope

So when you leave the while loop, e, being a local variable (if the loop is in a function) or a global variable (if not), is still available.

tl;dr: Python is not C.

Answer from kindall on Stack Overflow
๐ŸŒ
Reddit
reddit.com โ€บ r/learnpython โ€บ scope 'of except exception as e'
r/learnpython on Reddit: Scope 'of except Exception as e'
November 20, 2017 - Does e remain accessible in the 2nd print statement, or is it just localised within the try-except scope? try: foo() except Exception as e: print('Inside try-except:',e) print('Outside try-except:',e) Share ... No. (But you could set another variable inside the except block to e and its value will be retained if it was defined outside the these blocks) ... Thank you! I wanted to check this was the case, before trawling through all other areas of my code. ... This is an interesting little quirk of Python, it's not that the e has scope within the block it's that is actually deleted!
Discussions

Scope of caught exception instance in Python 2 and 3 - Stack Overflow
Since in Python variables are accessible outside of their loops and try-except blocks, I naively thought that this code snippet below would work fine because e would be accessible: try: int('s') More on stackoverflow.com
๐ŸŒ stackoverflow.com
Basic Python: Exception raising and local variable scope / binding - Stack Overflow
I have a basic "best practices" Python question. I see that there are already StackOverflow answers tangentially related to this question but they're mired in complicated examples or involve multi... More on stackoverflow.com
๐ŸŒ stackoverflow.com
April 30, 2010
variables - Python exceptions leaving local scope? - Stack Overflow
I recently discovered some odd behavior when writing a Python program. I have situation like follows: try: raise Exception("Meh!") except Exception as e: print e if e: print e To my More on stackoverflow.com
๐ŸŒ stackoverflow.com
Understanding scopes when raising exceptions in Python - Stack Overflow
My understanding was that I would escape from each level of scope until an exception handler was found. The Python tutorial says this: More on stackoverflow.com
๐ŸŒ stackoverflow.com
๐ŸŒ
Python
docs.python.org โ€บ 3.4 โ€บ reference โ€บ executionmodel.html
4. Execution model โ€” Python 3.4.10 documentation
April 3, 2010 - When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the blockโ€™s environment. When a name is not found at all, a NameError exception is raised. If the current scope is a function scope, and the name refers to a local variable that has not yet been bound to a value at the point where the name is used, an UnboundLocalError exception is raised.
๐ŸŒ
Data Freaker
datafreaker.com โ€บ home โ€บ day 7: python exception handling, scope and additional topics
Day 7: Python Exception Handling, Scope and Additional Topics
January 7, 2024 - Python provides a number of built-in exceptions, such as ZeroDivisionError, IndexError, and TypeError. You can use the try and except statements to handle exceptions. Scope is the region of a Python program in which a variable is accessible.
๐ŸŒ
Python
docs.python.org โ€บ 3 โ€บ builtins โ€บ exceptions.html
Built-in Exceptions โ€” Python 3.15.0 documentation
In Python, all exceptions must be instances of a class that derives from BaseException. In a try statement with an except clause that mentions a particular class, that clause also handles any exception classes derived from that class (but not ...
๐ŸŒ
Real Python
realpython.com โ€บ python-scope-legb-rule
Python Scope and the LEGB Rule: Resolving Names in Your Code โ€“ Real Python
July 16, 2026 - Built-in scope is a special scope that Python creates or loads whenever you run a script or open an interactive session. This scope contains names such as built-in functions, exceptions, and other attributes that are built into Python.
๐ŸŒ
Readthedocs
portingguide.readthedocs.io โ€บ en โ€บ latest โ€บ exceptions.html
Exceptions โ€” Conservative Python 3 Porting Guide 1.0 documentation
Since the traceback holds references to the values of all local variables, storing an exception in a local variable usually forms a reference cycle, keeping all local variables allocated until the next garbage collection pass. To prevent this issue, to quote from Pythonโ€™s documentation:
๐ŸŒ
DataCamp
datacamp.com โ€บ tutorial โ€บ exception-handling-python
Exception & Error Handling in Python | Tutorial by DataCamp | DataCamp
May 29, 2026 - With exception groups, nested exceptions are now easier to debug and handle, especially in complex workflows like multiprocessing or asynchronous tasks. Python 3.12 continued a long-running effort to make error messages more specific and actionable. For example, NameError messages now suggest similarly-named variables that are in scope, and attribute errors on common types give more targeted hints:
Find elsewhere
๐ŸŒ
docs.python.org
docs.python.org โ€บ 3 โ€บ reference โ€บ executionmodel.html
4. Execution model โ€” Python 3.14.8 documentation
When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the blockโ€™s environment. When a name is not found at all, a NameError exception is raised. If the current scope is a function scope, and the ...
๐ŸŒ
Python
docs.python.org โ€บ 3 โ€บ c-api โ€บ exceptions.html
Exception Handling โ€” Python 3.15.0 documentation
The error indicator is not the result of sys.exc_info(). The former corresponds to an exception that is not yet caught (and is therefore still propagating), while the latter returns an exception after it is caught (and has therefore stopped propagating).
๐ŸŒ
W3Schools
w3schools.com โ€บ python โ€บ python_try_except.asp
Python Try Except
Python Examples Python Compiler ... Training ... The try block lets you test a block of code for errors. The except block lets you handle the error....
๐ŸŒ
Python
bugs.python.org โ€บ issue43458
Issue 43458: Tutorial should mention about variable scope in try/except/finally - Python tracker
March 10, 2021 - This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide ยท This issue has been migrated to GitHub: https://github.com/python/cpython/issues/87624
Top answer
1 of 4
10

Does python have an elegant way to handle this?

To avoid exceptions from printing unbound names, the most elegant way is not to print them; the second most elegant is to ensure the names do get bound, e.g. by binding them at the start of the function (the placeholder None is popular for this purpose).

If not, what about an inelegant way?

try: print 'b is', b
except NameError: print 'b is not bound'

In a complicated function I'd prefer to avoid testing the existence of every local variable before I, for example, printed debug information about them

Keeping your functions simple (i.e., not complicated) is highly recommended, too. As Hoare wrote 30 years ago (in his Turing acceptance lecture "The Emperor's old clothes", reprinted e.g. in this PDF):

There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.

Achieving and maintaining simplicity is indeed difficult: given that you have to implement a certain total functionality X, it's the most natural temptation in the world to do so via complicated accretion into a few complicated classes and functions of sundry bits and pieces, "clever" hacks, copy-and-paste-and-edit-a-bit episodes of "drive-by coding", etc, etc.

However, it's a worthwhile effort to strive instead to keep your functions "so simple that there are obviously no deficiencies". If a function's hard to completely unit-test, it's too complicated: break it up (i.e., refactor it) into its natural components, even though it will take work to unearth them. (That's actually one of the way in which a strong focus on unit testing helps code quality: by spurring you relentlessly to keep all the code perfectly testable, it's at the same time spurring you to make it simple in its structure).

2 of 4
6

You can initialize your variables outside of the try block

a = None
b = None       
try:
    a = str(5)
    raise
    b = str(6)
except:
    print b
๐ŸŒ
Stack Abuse
stackabuse.com โ€บ python-exception-handling
Python Exception Handling
July 26, 2022 - This kind of exception is raised when your code is not properly indented. Instead of using curly braces for specifying scope, Python uses indentation, so it is important to get the indentation correct, which is the purpose of this exception.
Top answer
1 of 4
7

Just rethrow the exception when you get it:

try:
  #do stuff
except KeyboardInterrupt:
  raise
except:
  #do other stuff

You specify that you get an exception that you know that you want to handle differently than default case, but you don't know how to handle it - so you just pass it, by throwing it again.

Some working example:

#!/usr/bin/python2

if __name__ == "__main__":
    try:
        while True:
            try:
                print(1)
            except KeyboardInterrupt:
                raise
            except:
                print(2)
    except KeyboardInterrupt:
        print('end')

Please note that you shouldn't use except:, as this is a bad practice. except: used in this example is used in sole purpose to make things as simple as possible.

2 of 4
10

In general, you should not use except:, except for a few extremely rare circumstances, most notably log-and-reraise, or at the very top-level after which the program must end. Do not use it in libraries, or any non-top-level method, at all.

Instead, please use except Exception:. It catches most 'normal' errors, except a few special ones (KeyboardInterupt, 'SystemExit`, and some others). See https://docs.python.org/3.6/library/exceptions.html#exception-hierarchy.

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- ... (all other exceptions)

```

In most cases, I would even recommend against using except Exception:, and instead chose the most specific exception type(s) possible. This is because things like NameError, AttributeError, KeyError and the like are all subclasses of Exception. You can hide bugs (typos) in a very subtle way with this.

When dealing with network connections, you're most likely to want to use something like except socket.error:, or except LibrarySpecificErrorHere. Check the exception hierarchy for the networking library you use. You don't necessarily have to use the leaf exception classes.

With a wider except clause, more bugs will be hidden by it. Therefore: have as little code as possible in the try block, and have the exception classes as specific as possible.

Consider this:

bars = []
for foo in foos:
    try:
        bar = foo.bar
    except AttributeError:
        # Sometimes we get a `foo` that does not have a `bar` set yet.
        pass
    else:
        bar.apend(bar)

versus

bars = []
for foo in foos:
    try:
        bar = foo.bar
        bars.apend(bar)
    except AttributeError:
        # Sometimes we get a `foo` that does not have a `bar` set yet.
        pass

Did you notice the spelling error in append (I 'accidentally' wrote apend). You are smart, so you did notice. But Python misses it in the second case, while in the first case the scope of the exception handling is smaller, and thus a bug was lurking outside the scope of the handling, got out, and you saw it.

The same goes for this piece of code:

bars = []
for foo in foos:
    try:
        bar = fo.bar
    except:
        # Sometimes we get a `foo` that does not have a `bar` set yet.
        pass
    else:
        bars.append(bar)

Oopsie, did I just accidentally ignore a NameError?

Again: except: catches all exceptions, even the ones you are not supposed to catch (SystemExit, KeyboardInterupt, ...). In general, the application is supposed to be dead after any of those exceptions are raised. Be very specific in handling exceptions, make sure as few bugs as possible get hidden by overly aggressive exception handling.