例外の階層構造
Pythonでは、すべての例外はBaseExceptionクラスから派生しています。つまり、Pythonの例外はすべてBaseExceptionまたはそのサブクラスのインスタンスです。
BaseExceptionクラスは例外階層の最上位のクラスであり、__str__や__repr__など、すべての例外が使用できる共通のメソッドを提供します。しかし、BaseExceptionを直接コードで使用するべきではありません。なぜなら、それは範囲が広すぎて、あらゆる種類の例外を捕捉してしまうからです。代わりに、BaseExceptionのサブクラスであるより具体的な例外クラスを使用するべきです。Pythonは多くの組み込み例外クラスを提供しており、自分自身でカスタムの例外クラスを作成することもできます:
BaseException
├── BaseExceptionGroup
├── GeneratorExit
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ExceptionGroup [BaseExceptionGroup]
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── StopAsyncIteration
├── StopIteration
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── BytesWarning
├── DeprecationWarning
├── EncodingWarning
├── FutureWarning
├── ImportWarning
├── PendingDeprecationWarning
├── ResourceWarning
├── RuntimeWarning
├── SyntaxWarning
├── UnicodeWarning
└── UserWarningThe hierarchy of built-in exception classes in Python (from the official Python docs).
つまり、FileNotFoundErrorはOSErrorのサブクラスであり、OSErrorはExceptionクラスのサブクラスで、そのExceptionクラス自体がBaseExceptionから継承されています。
例外の階層構造を尊重する
複数のexcept文を使用する際には、Pythonの例外階層構造を尊重することが重要です。つまり、Exceptionのような基底の例外クラスを捕捉する場合、より具体的な例外をその前に捕捉する必要があります。もし基底クラスの後により具体的なクラスのexcept文を書いても、そのブロックは実行されません:
try:
# 例外を発生させる可能性のあるコード
except ValueError:
# ValueErrorを処理する
except Exception:
# その他の例外を処理するこの例では、ValueError例外が最初に捕捉されます。コードが他のExceptionから派生した例外を発生させた場合、2つ目のexceptブロックのコードが実行されます。しかし、Exceptionを直接捕捉することはおすすめできません。これは範囲が広すぎて、あらゆる種類の例外を捕捉してしまうからです。可能な限り、より具体的な例外を捕捉する方がよいでしょう:
try:
...
except TypeError:
...
except ValueError:
...
except Exception:
...tryブロック内のコードがTypeErrorやValueErrorを発生させた場合、それぞれ対応するexceptブロックが実行されます。コードが他のExceptionから派生した例外を発生させた場合、3つ目のexceptブロックのコードが実行されます。
もし基底の例外を最初のexceptブロックに置くと、残りのブロックは決して実行されません:
try:
# 例外を発生させる可能性のあるコード
except Exception as e: # これはすべてのエラーを捕捉します
print(f'An error occurred: {e}')
except ValueError: # これは実行されません
print('A ValueError occurred')
except TypeError: # これは実行されません
print('A TypeError occurred')この例では、tryブロックは任意の種類の例外を発生させる可能性があります。しかし、最初のexcept文が基底のExceptionクラスを捕捉しているため、発生したすべての例外を捕捉します。エラーメッセージが表示され、残りのexcept文は実行されません。
チャレンジ:ファイルを解析する
あなたは大量のデータを扱うスタートアップでソフトウェアエンジニアとして働いています。最近、チームはデータの保存と処理にJSONファイルを使用することを決定しました。このデータはあなたのスタートアップの運営にとって非常に重要であり、これらのJSONファイルを読み込み解析するPythonプログラムを作成するのがあなたの仕事です。
しかし、大量のデータと複数の人が関わっているため、いくつかのエラーが頻繁に発生します:
データファイルが存在しない場合があります。
データファイルが空の場合があります。
データファイルに有効なJSONでないデータが含まれている場合があります。
あなたのPythonプログラムは、これらの例外をクラッシュせずに処理し、データチームが迅速に問題を特定し修正できるよう、意味のあるエラーメッセージを提供する必要があります。
あなたのプログラムは、ファイル名を表す文字列を入力として受け取り、次のケースに対応する必要があります:
ファイルが存在しない場合、
FileNotFoundErrorを発生させ、適切なエラーメッセージを表示します。ファイルが空の場合、
ValueErrorを発生させ、適切なエラーメッセージを表示します。ファイルの内容が有効なJSONでない場合、
json.JSONDecodeError(ValueErrorのサブクラス)を発生させ、適切なエラーメッセージを表示します。この例外を発生させる際には、docとposの属性をパラメータとして提供する必要があります。
これらの例外はそれぞれ独立して処理されるべきであり、あなたのプログラムは各ケースに対して具体的なエラーメッセージを提供する必要があります。
入力 | 出力 |
|---|---|
nonexistent_file.json | FileNotFoundError: File 'nonexistent_file.json' does not exist. |
empty_file.json | ValueError: File 'empty_file.json' is empty. |
invalid_json.json | JSONDecodeError: Content of file 'invalid_json.json' is not valid JSON.: line 1 column 1 (char 0) |
success.json | File 'success.json' has been successfully parsed as valid JSON. |
Constraints
Time limit: 2 seconds
Memory limit: 512 MB
Output limit: 1 MB