Python 全攻略 第八章 - Error Handling and Exceptions

Python 全攻略第八章 例外處理 (Sec. 115~119)


Image by Thomas Malyska from Pixabay

例外處理簡介

  • Exception handling refers to the way that errors and unexpected events are handled during the execution of a program.
  • Exception handling is essential for writing robust and reliable code.
  • It enables programmers to handle errors and exceptions in a structured and controlled manner.
  • There are 2 approaches(coding style) to handle Exceptions
    • LBYL(Look Before You Leap)
      • Drawback1: This decreases the readability of codes
      • Drawback2: Take a longer time to execute the code
    • EAFP(Easier to Ask Forgiveness than Permission) In Python, we do EAFP more.
      • Advantage1: Reduce race conditions → Multiple threads to access or change the same object.
      • Advantage2: Avoid errors during condition checking and the actual execution.
    • EAFP is not perfect. For the following cases, we would perfer LBYL
      • For a task that is computationally heavy and time consuming.
      • For an important task that is costy to trace back. (執行後無法回溯-例如刪檔)
  • A great article for your reference: LBYL vs EAFP: Preventing or Handling Errors in Python
# LBYL Approach - 先預測錯誤並加以防堵 (if ... else ...)
def safe_divide_1(x: int, y: int):
    if y == 0:
        print("Divide by 0 attempt detected.")
        return None
    else:
        return x / y

# EAFP Approach - 直接執行,有錯再由異常處理來處理 (try ... except ...)
def safe_divide_2(x: int, y: int):
    try:
        return x / y
    except ZeroDivisionError:
        print("Divide by 0 attempt detected.")
        return None
# This is not real codes - 僅為示意用途

# LBYL - 預想很多可能出錯的狀況
def save_a_file():
    result = save_prefs()
    if result == 'error':
        print('Preference not saved.')
        return
    result = save_text()
    if result == 'error':
        print('Not enough memory.')
        return
    result = save_format()
    if result == 'error':
        print('Format not saved.')
        return

# EAFP - 程式碼較少,比較簡單
def save_a_file():
    try:
        save_prefs()
        save_text()
        save_format()
    except:
        print('Something went wrong...')

例外處理的語法說明

  • Python 官方的 Exception 詳細列表,可參考 這裡
  • need to have try & except pair
  • As is optional for except
  • Except will run if try has an error
  • Else and finally are also optional

try1

try:
    result = 10 + "10"
except:
    print("Something went wrong")
finally:
    print(result)
# we can have multiple except sections
try:
    f = open("testfile.txt", "r")
    f.write("write a test line")
except TypeError:
    print("There is a typo")
except OSError:
    print("There is an OS Error")
except:
    print("Whatever other errors will go here.")
finally:
    print("This will run no matter what.")
There is an OS Error
This will run no matter what.
def ask_for_int():
    while True:
        try:
            result = int(input("Enter a number here: "))
        except ValueError as ve:
            print(ve) # print error message
            print("Please try again.")
        else:
            print("Good job!")
            return result

ask_for_int()

常見的錯誤與例外

# These errors are all in builtins module -> print(dir(__builtins__))
# 1 Type error
try:
    for i in ["a", "b", "c"]:
        print(i ** 2)
except TypeError:
    print("We have type error!!")
We have type error!!
# 3 Name error
print(x)
# 4 ResursionError -> stack overflow
def hello():
    print("hello")
    hello()

hello()
# 5 KeyError(dict, tuple) and 6 IndexError(list) -> LookUpError 的 subclass
lst = [1, 2, 3]
print(lst[10])
try:
    a = {1: 'hello', 2: 'how', 3: 'are', 4: 'you'}
    print(a[5])
except KeyError:
    print("We've got a KeyError")
We've got a KeyError
# 7 Value error 無法把字串換成整數
int(250)
int("hello")
# 8 FileNotFound error
try:
    with open("hello.txt") as f:
        print(f.read())
except FileNotFoundError:
    print("File not found")
File not found

如何自訂例外

  • In Python, all exceptions must be instances of class derived from BaseException
  • If a function wants to throw and exception, then we use the keyword → raise
# 在程式執行時會發生的 error,我們選擇 RuntimeError 當做 parent class
class NegativeNumberException(RuntimeError):
    def __init__(self, age):
        super().__init__()
        self.age = age
        if( age < 0):
            print("This is not a valid age!!!")

def enter_age(age):
    if age < 0:
        raise NegativeNumberException(age)

    if age % 2 == 0:
        print("Your age is an even number.")
    else:
        print("Your age is odd.")

例外處理的先後順序

  • The order of putting exception codes matters
  • It could be affected by the hierarchical structure of exception classes.

try:
    lst = [1, 2, 3]
    print(lst[4]) # Index Error
except LookupError:
    print("There is a look up error")
except IndexError:
    print("There is an index error")