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 OverflowSimple: 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.
in except ... as e, the e will be drop when Jump out of try except, Whether or not it was defined before.
When an exception has been assigned using as target, it is cleared at the end of the except clause.
refer to offical website link: https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
Scope of caught exception instance in Python 2 and 3 - Stack Overflow
Basic Python: Exception raising and local variable scope / binding - Stack Overflow
variables - Python exceptions leaving local scope? - Stack Overflow
Understanding scopes when raising exceptions in Python - Stack Overflow
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).
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
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.
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.