######################################Error Handling#####################################
# In the process of running the program, if an error occurs, an error code can be returned by agreement in advance,
# so that you will know whether there is an error and the reason caused the error. It is very common to return error
# codes in calls provided by the os.
# For example, a function that opens a file open() returns the file descriptor (that is, an integer) when successful,
# and returns when there is an error -1.
# It is very inconvenient to use the error code to indicate whether there is an error, because the normal result that
# the function itself should return is mixed with the error code, so the caller must use a lot of code to determine
# whether there is an error:
# err.py
def foo(s):
return 10 / int(s) #ZeroDivisionError: division by zero
def bar(s):
return foo(s) * 2
def main():
bar(0)
# main() #ZeroDivisionError: division by zero
# Once an error occurs, it must be reported level by level until a function can handle the error (for example,
# output an error message to the user).
# So high-level languages usually have a built-in set try...except...finally...of error handling mechanisms, and Python
# is no exception.
#########################################try############################################
# Let's use an example to see try the mechanism:
def fun(n):
try:
print('try...')
r = 10 / n
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
except TypeError as e:
print('except:',e)
finally:
print('finally...')
print('END')
fun(0)
# try...
# except: division by zero
# finally...
# END
fun(1)
# try...
# result: 10.0
# finally...
# END
fun('a')
# try...
# except: unsupported operand type(s) for /: 'int' and 'str'
# finally...
# END
print('\n')
# In addition, if no error occurs, you can except add one after the statement block else, when no error occurs,
# the else statement will be executed automatically:
def fun(n):
try:
print('try...')
r = 10 / int(n)
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
fun(1)
# try...
# result: 10.0
# no error!
# finally...
# END
fun('a')
#try...
# ValueError: invalid literal for int() with base 10: 'a'
# finally...
# END
print('\n')
# There is also a huge advantage of using try...except catching errors, that is, you can call across multiple layers,
# such as function main() calls bar(), bar() calls foo(), and the result is foo() an error. At this time, as long as it
# main() is caught, it can be processed:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main(n):
try:
bar(n)
except Exception as e:
print('Error:', e)
finally:
print('finally...')
main('0')
# Error: division by zero
# finally...
main('s')
# Error: invalid literal for int() with base 10: 's'
# finally...
# That is, you don't need to catch errors at every possible place, just at the appropriate level. try...except...finally
# In this way, the trouble of writing is greatly reduced .
print('\n')
#######################################call stack##########################################
# If the error is not caught, it just keeps going up and is caught by the Python interpreter, prints an error message,
# and the program exits.
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
# main() # Traceback (most recent call last):
print('\n')
# The outputs are as follows:
# Traceback (most recent call last):
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 130, in
# main()
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 128, in main
# bar('0')
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 125, in bar
# return foo(s) * 2
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 122, in foo
# return 10 / int(s)
# ZeroDivisionError: division by zero
# When an error occurs, be sure to analyze the wrong call stack information in order to locate the wrong location.
####################################### log error ###########################################
# If you don't catch the error, you can have the Python interpreter print the error stack, but the program is terminated.
# Now that we can catch the error, we can print the error stack, analyze the cause of the error, and let the program
# continue.
# Python's built-in logging module makes logging error messages very easy:
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
print('END')
main()
# ERROR:root:division by zero
# Traceback (most recent call last):
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 160, in main
# bar('0')
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 156, in bar
# return foo(s) * 2
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 153, in foo
# return 10 / int(s)
# ZeroDivisionError: division by zero
# END
# Through configuration, loggingerrors can also be recorded in the log file, which is convenient for later investigation.
print('\n')
#######################################throw error########################################
# Because errors are classes, catching an error is catching an instance of that class. So errors are not created
# out of thin air, but are intentionally created and thrown. Python's built-in functions throw many types of errors,
# and functions we write ourselves can throw errors as well.
# If you want to throw an error, you can first define an error class according to your needs, choose a good inheritance
# relationship, and then use the raise statement to throw an error instance:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
# foo('0')
# Traceback (most recent call last):
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 195, in
# foo('0')
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 192, in foo
# raise FooError('invalid value: %s' % s)
# __main__.FooError: invalid value: 0
# Only define our own error types when necessary. If you can choose one of Python's built-in error types
# (eg ValueError, TypeError), try to use Python's built-in error types.
print('\n')
# Finally, let's look at another way of error handling:
def foo(s):
n = int(s)
if n==0:
raise ValueError('invalid value: %s' % s)
return 10 / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise
bar()
# The outputs are as follows:
# ValueError!
# Traceback (most recent call last):
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 222, in
# bar()
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 217, in bar
# foo('0')
# File "C:\workspace\Python\python_tutorials\10_debug\2.handlerror.py", line 212, in foo
# raise ValueError('invalid value: %s' % s)
# ValueError: invalid value: 0