Python exception handling

Keywords: Python

1, What is an exception

An exception is a signal of a program error. Once an error occurs in the program, an exception will be generated. If it is not handled in the program, an exception will be thrown, and the operation of the program will be terminated.

In Python, the exceptions triggered by errors are as follows:

There are two kinds of errors. One is syntax error, which should be corrected before the program runs:

>>> if  
File "<stdin>", line 1
   if
    ^
SyntaxError: invalid syntax

The other is logic error, which can be modified as soon as possible after it occurs. Common logic errors are as follows:

# TypeError: numeric type cannot be added to string type
1 + '2'  

# ValueError: cannot convert to int type when the string contains a non numeric value
num = input(">>: ")  # Enter hello
int(num)
 
# NameError: a nonexistent name x is referenced
x
 
# IndexError: index exceeds the limit of the list
lis = ['jason', 'tony']
lis[3]
 
# KeyError: a nonexistent key is referenced
dic = {'name': 'jason'}
dic['age']
 
# AttributeError: the referenced attribute does not exist
class Foo:
    pass

Foo.x
 
# ZeroDivisionError: divisor cannot be 0
1 / 0

2, Exception handling

In order to ensure the fault tolerance and reliability of the program, that is, there is a corresponding processing mechanism to prevent the program from crashing when encountering errors, we need to handle exceptions. The basic syntax format of processing is as follows:

try:
    Detected code block
except Exception type:
    When an exception is detected, the logic of this location is executed

Examples are as follows:

try:
    print('start...')
    print(x)  # A nonexistent name is referenced, and the exception NameError is triggered
    print('end...')
except NameError as e:
    print('The outliers are:%s' % e)
print('run other code...')

"""as Syntax assigns the value of an exception type to a variable e,So we print e You can know the cause of the error"""

The execution results are:

start...
The outliers are: name 'x' is not defined
run other code...

Once an exception occurs in the original program, it will end as a whole. After exception handling, when an exception occurs in the detected code block, the code after the exception location in the detected code block will not be executed. Instead, the except sub code block matching the exception will be executed, and the rest of the code will run normally.

When different types of exceptions may be triggered in the detected code block, for different types of exceptions:

If we want to use different logic processing, we need to use multi branch except (similar to multi branch elif, matching from top to bottom, and no other matching will be made once the matching is successful):

try:
    Detected code block
except NameError:
    trigger NameError Processing logic corresponding to
except IndexError:
    trigger IndexError Processing logic corresponding to
except KeyError:
    trigger KeyError Processing logic corresponding to

Examples are as follows:

def convert_int(obj):
    try:
        res = int(obj)
    except ValueError as e:
        print('ValueError: %s' % e)
        res = None
    except TypeError as e:
        print('TypeError: %s' % e)
        res = None
    return res

convert_int('jason')  # ValueError: invalid literal for int() with base 10: 'jason'
convert_int({'n': 1})  # TypeError: int() argument must be a string, a bytes-like object or a number, not 'dict'

If we want to handle multiple types of exceptions with one logic, we can put multiple exceptions into a tuple and match them with an exception:

try:
    Detected code block
except (NameError, IndexError, TypeError):
    trigger NameError or IndexError or TypeError Processing logic corresponding to

Examples are as follows:

def convert_int(obj):
    try:
        res = int(obj)
    except (ValueError, TypeError):
        print('argument must be number or numeric string')
        res = None
    return res

convert_int('eason')  # argument must be number or numeric string
convert_int({'n': 1})  # argument must be number or numeric string

If we want to catch all exceptions and handle them with one logic, Python provides a universal Exception exception:

try:
    Detected code block
except NameError:
    trigger NameError Processing logic corresponding to
except IndexError:
    trigger IndexError Processing logic corresponding to
except Exception:
    Other types of exceptions are handled by the logic here

An else can be followed by a multi branch except (else must follow except and cannot exist alone). Only when the detected code block does not trigger any exceptions will the sub code block of else be executed:

try:
    Detected code block
except Exception type 1:
    pass
except Exception type 2:
    pass
......
else:
    No code block executed when an exception occurs

In addition, try can also be used with finally. Grammatically, finally must be placed after else, but you can use the form of try except finally or try finally directly;

Whether the detected code block triggers an exception or not, the finally sub code block will be executed. Therefore, some resource recycling operations are usually performed in the finally sub code block, such as closing the open file, closing the database connection, etc.

try:
   Detected code block
except Exception type 1:
   pass
except Exception type 2:
   pass
......
else:
   No code block executed when an exception occurs
finally:
   A block of code that executes whether or not an exception occurs

Examples are as follows:

f = None
try:
    f = open('db.txt', 'r', encoding='utf-8')
    s = f.read().strip()
    int(s)  # If the string s contains a non number, the exception ValueError will be triggered
    f.close()  """If the above code triggers an exception, it is impossible to execute the code here, and the operation of closing the file should be placed in the finally in"""
finally:
    if f:  # If the file exists, the value of f is not None
        f.close()

When it does not comply with the syntax or logic rules of the Python interpreter, it is various types of exceptions actively triggered by the Python interpreter. For violations of various rules customized by the programmer, the programmer needs to explicitly trigger the exception. This uses the raise statement. After raise, it must be an exception class or an exception instance:

class Student:
    def __init__(self, name, age):
        if not isinstance(name, str):
            raise TypeError('name must be str')
        if not isinstance(age, int):
            raise TypeError('age must be int')

        self.name = name
        self.age = age

stu1 = Student(4573, 18)  # TypeError: name must be str
stu2 = Student('jason', '18')  # TypeError: age must be int

When the built-in exception is not enough, we can define the exception class by inheriting the built-in exception class:

You can define a new Exception by inheriting Exception:

class PoolEmptyError(Exception):  
    def __init__(self, value = 'The proxy source is exhausted'):  # The initialization method can be customized
        super(PoolEmptyError, self).__init__()
        self.value = value

    def __str__(self):  # You can define this method to customize the format of printing exception values when an exception is triggered
        return '< %s >' % self.value


class NetworkIOError(IOError):  # You can also extend a related exception based on a specific exception
    pass


raise PoolEmptyError  # __main__.PoolEmptyError: < The proxy source is exhausted >
raise NetworkIOError('connection not permitted')  # __ main__.NetworkIOError: connection denied

Finally, Python also provides an assertion statement assert expression, which determines that the expression expression is true, otherwise an exception AssertionError will be triggered, which has the same semantics as raise if not, as follows:

age = '18'

# If the return value of the expression isinstance(age, int) is False, an exception AssertionError is triggered
assert isinstance(age, int)

# Equivalent to
if not isinstance(age, int):
	raise AssertionError

3, When to use exception handling

After understanding the exception handling mechanism, in order to improve the fault tolerance and reliability of the program, readers may mistakenly think that try... Exception... Should be added to the program as much as possible, which is actually over consuming the readability of the program, because try... Exception is originally an additional logic you add to the program, which has little to do with your main work.

If the condition of error occurrence is "predictable", we should use if to "prevent", as follows:

age = input('input your age>>: ').strip()
if age.isdigit():  # It can be predicted that int(age) will not trigger an exception only if the string age is a number,
    age = int(age)
else:
    print('You must enter the number')

If the condition of error occurrence is "unpredictable", that is, the exception must be triggered, then we should use the try...except statement to handle it.

For example, when we write a function to download web content, it is normal for exceptions such as network delay to occur, and we simply cannot predict what conditions will be met before the delay will occur, so we can only use the exception handling mechanism:

import requests
from requests.exceptions import ConnectTimeout

def get(url):
    try:
        response = requests.get(url, timeout=3)  # If the download fails for more than 3 seconds, a ConnectTimeout exception will be triggered
        res = response.text
    except ConnectTimeout:
        print('Connection request timed out')
        res = None
    except Exception:
        print('Other network exceptions')
        res = None
    return res

get('https://www.python.org')

Summary:

  • The code that may have errors needs to be monitored;
  • The less code to be monitored, the better;
  • The lower the frequency of exception capture, the better;

Posted by behicthebuilder on Mon, 22 Nov 2021 15:14:48 -0800