Руководство по исключению Python

1- Что такое Exception?

Для начала посмотрим следующий иллюстрированный пример:
В данном примере есть код с ошибкой по причине деления на 0. Из-за деления на 0 происходит исключение: ZeroDivisionError
helloExceptionExample.py
print ("Three")

# This division no problem.
value = 10 / 2

print ("Two")

# This division no problem.
value = 10 / 1
 
print ("One")

d = 0

# This division has problem, divided by 0.
# An error has occurred here.
value = 10 / d

# And the following code will not be executed.
print ("Let's go!")
Результат запуска примера:
Вы можете увидеть уведомление ошибки на экране Console, уведомление ошибки очень понятное, на какой строке кода это произошло.
Посмотрим потом программы на изображении ниже.
  • Программа запущена как обычно с шагов (1),(2) до (6)
  • В шаге (7) происходит проблема при делении на 0. Программа завершается.
  • В шаге (8) код не был выполнен.

Мы изменим код в примере выше.

helloCatchException.py
print ("Three")

value = 10 / 2

print ("Two")

value = 10 / 1
 
print ("One")

d = 0


try :
    # Это деление имеет проблему, деление на 0.
    # Здесь происходит ошибка (ZeroDivisionError).
    value = 10 / d
    
    print ("value = ", value)
    
except ZeroDivisionError as e :
    
    print ("Error: ", str(e) )
    print ("Ignore to continue ...")


print ("Let's go!")
И результаты запуска примера:
Мы объясним используя иллюстрацию ниже о потоке программы.
  • Шаги (1)-(6) совершенно нормальны.
  • Исключение происходит в шаге (7), проблема при делении на 0.
  • Сразу он перепрыгивает на выполнение команды в блоке except, шаг (8) пропускается.
  • Шаги (9), (10) выполняются.
  • Шаг (11) выполняется.

2- Иерархия исключений

Это модель иерархии исключений в  Python.
  • Самый высший класс это BaseException
  • Прямыми подклассами являются Exception и KeyboardInterrupt, ..


Готовые  Exception в Python обычно получены из (derived)   BaseException (Расширены из  BaseException). В то время как exception пользвателя (программиста) должны быть унаследованы из класса Exception или из его подкласса.

3- Обработка исключений через try-except

Мы напишем класс exception унаследованный из класса  Exception.
See more:
Функция  checkAge для проверки возраста, если возраст меньше 18 или больше 40, выбрасывается исключение.
ageexception.py
# Python 3.x
class AgeException(Exception):
    
    def __init__(self, msg, age ):
        super().__init__(msg) 
        self.age = age
        
        
class TooYoungException(AgeException):
    
    def __init__(self, msg, age):
        super().__init__(msg, age)     
        

class TooOldException(AgeException):
    
    def __init__(self, msg, age):
        super().__init__(msg, age)    
         
# Функция проверки возраста, может выбросить исключение.
def checkAge(age):
  
    if (age < 18) :
        # Если возраст менее 18, выбросится исключение.
        # Фукнция завершится здесь.
        raise TooYoungException("Age " + str(age) + " too young", age)
    
    elif (age > 40) :
        # Если возраст старше 40, выбросится исключение.
        # Фукнция завершится здесь.       
        raise TooOldException("Age " +  str(age) + " too old", age);

    # Если возраст в рамках 18-40.
    # Этот код будет выполнен.
    print ("Age " +  str(age) + " OK!");
Пример:
tryExceptDemo1.py
import ageexception
from ageexception import AgeException
from ageexception import TooYoungException
from ageexception import TooOldException


print ("Start Recruiting ...")

age = 11

        
print ("Check your Age ", age)

try :


    ageexception.checkAge(age)

    print ("You pass!")

 
except TooYoungException as e  :


    print("You are too young, not pass!")    
    print("Cause message: ", str(e) )
    print("Invalid age: ", e.age)


except  TooOldException as e :

 
    print ("You are too old, not pass!")
    print ("Cause message: ", str(e) )
    print("Invalid age: ", e.age)
   
 
Запуск примера:
В примере ниже, мы обработаем все собранные исключения на уровне выше. На уровне выше обработаются все эти исключения и унаследованные исключения.
tryExceptDemo2.py
import ageexception
from ageexception import AgeException
from ageexception import TooYoungException
from ageexception import TooOldException


print ("Start Recruiting ...")

age = 51

      
print ("Check your Age ", age)

try :


    ageexception.checkAge(age)

    print ("You pass!")


except AgeException as e  :


    print("You are not pass!")  
    print("type(e): ", type(e) )
    print("Cause message: ", str(e) )
    print("Invalid age: ", e.age)
 
Запуск примера:

4- Блок try-except-finally

Выше мы ознакомились с обработкой исключений через блок  try-except. Полная обработка исключения использует  try-except-finally. Блок  finally всегда выполняется, несмотря не то, происходит исключение в блоке try или нет.
try - except - finally
try :

    # Сделать что-то здесь.
except Exception1 as e :

    # Сделать что-то здесь.
except Exception2 as e :

    # Сделать что-то здесь.
finally :

    # Блок finally всегда выполняется.
    # Сделать что-то здесь.
Пример:
tryExceptFinallyDemo.py
def toInteger(text) :
       
    try :
    

        print ("-- Begin parse text: ", text)

        # Исключение может быть выброшено здесь (ValueError).
        value = int(text)

        return value

    except ValueError as e :
     
        # В случае 'text' не является числом.
        # Блок 'except' будет выполнен.          
        print ("ValueError message: ", str(e))
        print ("type(e): ", type(e))

        # Возвращает 0 если появляется ошибка ValueError.
        return 0

     
    finally :
   

        print ("-- End parse text: " + text)

    
 
text = "001234A2"

value = toInteger(text)

print ("Value= ", value)
Запуск примера:
Это поток программы. Блок finally всегда выполняется.

Команда pass

Если ничего не хотите обрабатывать в блоке 'except' или  'finally' вы можете использовать команду  'pass' (pass statement). Команда  pass ничего не делает, она похожа на команду null.
passStatementExample.py

print ("Three")

try :
   
    value = 10 / 0;
   
except Exception as e:
   
    pass

print ("Two")   

print ("One")

print ("Let's go")

5- Перевыбросить исключение

При обработке исключения вы можете словить это исключение и обработать или перевыбросить (rethrow) его наружу.
reRaiseExceptionDemo1.py


def checkScore(score) :
    
    if score < 0 or score > 100:
        raise Exception("Invalid Score " + str(score) )
    


def checkPlayer(name, score):
    
    try :
        
        checkScore(score)
     
    except Exception as e :
        
        print ("Something invalid with player: ",name, ' >> ', str(e) )
        
        # re throw exception.
        raise    
 
 
# ------------------------------------------


checkPlayer("Tran", 101)          
Например, словить инсключение и перевыбросить (rethrow) другое исключение.
reRaiseExceptionDemo2.py


def checkScore(score) :
  
    if score < 0 or score > 100:
        raise Exception("Invalid Score " + str(score) )
  


def checkPlayer(name, score):
  
    try :
      
        checkScore(score)
    
    except Exception as e :
      
        print ("Something invalid with player: ",name, ' >> ', str(e) )
      
        # throw new exception.
        raise Exception("Something invalid with player: "+ name + " >> "+ str(e))  


# ------------------------------------------


checkPlayer("Tran", 101)          

6- Обернуть Exception в другом Exception 

Python позволяет ловить иключение, и выбрасывать новое исключение, новое исключение может хранить информацию начального исключения, в который вы можете иметь доступ через атрибут  __cause__.
try :

    # Сделать что-то здесь....
except Exception as e :

    raise OtherException("Message...") from e
Смотрите полный пример:
wrapExceptionDemo.py
# Python 3.x:
# Исключение пола.
class GenderException(Exception):
    
    def __init__(self, msg):
        super().__init__(msg)     

# Исключение языка.
class LanguageException(Exception):
    
    def __init__(self, msg):
        super().__init__(msg)     


class PersonException(Exception):
    
    def __init__(self, msg):
        super().__init__(msg)

# Эта функция может выбросить GenderException.
def checkGender(gender):
    
    if gender != 'Female' :
        raise GenderException("Accept female only")

# Эта функция может выбросить LanguageException.
def checkLanguage(language):  
      
    if language != 'English' :
        raise LanguageException("Accept english language only")
    

def checkPerson(name, gender, language):
    
    try : 
        # Может выбросить GenderException.
        checkGender(gender)
        # Может выбросить LanguageException.
        checkLanguage(language)
    except Exception as e:
        # Ловит exception и выбрасывает другие исключения.
        # Новое исключение имеет информацию __cause__ = e.
        raise PersonException(name + " does not pass") from e
    
# --------------------------------------------------------
try  :
    
    checkPerson("Nguyen", "Female", "Vietnamese")
    
except PersonException as e:
    
    print ("Error message: ", str(e) )   
    
    # GenderException или LanguageException
    cause = e.__cause__  
    
    print ('e.__cause__: ', repr(cause))
    
    print ("type(cause): " , type(cause) )
    
    print (" ------------------ ")
    
    if type(cause) is GenderException :
        
        print ("Gender exception: ", cause)
        
    elif type(cause) is LanguageException:
        
        print ("Language exception: ", cause)