こんにちは、Pythonエンジニア育成推進協会 顧問理事の寺田です。私は試験の問題策定とコミュニティ連携を行う立場です。
プログラミングをする人は必ずと言っていいほど長文、短文ともにエラーメッセージに遭遇していて、どんなに普段からプログラミングに慣れていたとしてもエラーに悩まされ続けます。
ですがエラーが出ることを怖がってはいけません。
エラーにどう向き合うかが初心者を脱する壁になるはずですので、エラーが出てもあわてずに適切に処理し、エラーの内容によっては回避する手法を学んでいくことが大事になってきます。
そこで今回はPythonとエラーについてお話したいと思います。
■Pythonのエラーは2種類
Pythonで発生するエラーの種類は大きく分けると2種類あり、それぞれ原因と対処の仕方が異なります。
1)プログラム自体がまったく動かないエラー
直さないと動かないので、絶対に直さなければならないエラーです。
2)例外
プログラムの実行はできますが、例えばプログラムに受け渡すデータに問題がある、受け渡す引数が違うなどの何かしらの条件によって処理が止まってしまうエラーです。
1)プログラム自体が全く動かないエラー
まずこれに関しては原因が2つに分けられます。
①文法エラー(SyntaxError)
文法的に記述が間違っているために、プログラム自体が間違っていると判断されて起きるエラーです。
例えば括弧の閉じ忘れや、許されていない記述方法がされているなど、文法的に間違ったことが行われているときに起きます。
②インデントエラー(IndentationError)
Pythonではブロック構造をインデントによって表すため、インデントが必要な場所が必ず決まっています。
例えばif文の後には必ずインデントが必要になりますが、この時、インデントの箇所がずれているとエラーになります。Pythonのコーディング規約であるPEP8では、インデントは半角4文字で表現するように定められています。
<参考リンク>
・PEP8
・PythonZen & PEP 8 検定試験(無料)
この2点に関しては、慣れると見るだけで何となく直し方がわかってくるようになります。
ちなみに、Python3.10以降は、SyntaxErrorやIndentationErrorは比較的丁寧な形でエラーメッセージを出してくれるようになっていますので、エラーの箇所が見つけやすくなっています。メッセージで示されているところを、必ず適切な形に修正して動くプログラムにしましょう。
2)例外(exception)
問題は動かない時より、プログラムが動いているのに止まってしまう「例外」です。
例えば整数の1と文字列の「”1″」は型が違いますので、+記号では連結できず、Typeエラーという例外が発生します。
こうした例外によるエラーは動かしてみないとわからないため、とても厄介ですし、このエラーが発生したときにはそれぞれの原因を特定し、それに応じた例外処理を行う必要も出てきます。
- 例外のエラーメッセージの読み方
例外が発生したとき、Tracebackという長い英語で書かれた文字列がコンソールに表示されます。長文なので、一見厄介なものに見えますが、なぜ例外が出てしまったのか、その場所を特定しやすくするために丁寧に書かれているだけで、厄介なものではありません。
十数行ほど表示されますので、読み解くのは難しいですし、最初は時間がかかるかもしれませんが、慣れてくれば何が悪かったのか、すぐわかるようになります。
Tracebackを読み解くにあたって一番のポイントは、とにかく最後の行を読むことです。最後の行より上の部分にはプログラムの呼び出し関係が表示されていますので、最初のうちは見なくても構いません。
最後の行には例えば、「TypeError:unsupported operand~」というような英語で書かれた文章が表示されるのですが、まずはこれを翻訳したり、検索したりといった手段を駆使しつつ頑張って読み解いてみましょう。最近ではChatGPTのようなAIに聞くのもありだと思います。こうして解決方法を見つけていきます。
慣れてきたら今度は最終行以前の行も読んでみて、示された箇所のコードを見てみましょう。
ところで、エラーの中にはライブラリの中で起きたものが表示されることがありますが、ライブラリの中で起きたエラーの原因は本当にわかりにくくなります。
「それを書いたのは自分じゃない!」と思うかもしれませんが、そのエラーの原因は、その直前で行われたデータの渡し方やライブラリの使い方に問題がある可能性があります。Tracebackをさかのぼることで、そのライブラリの中でエラーが起きた原因となっている自分のコードを特定することができます。
- 例外処理の書き方
try-except文を使えば、例外が起きたときに、別の処理を行うということをプログラム上に書くことができます。
例えば数字の1と文字列の1を結合したいと思っても型が違うため、TypeErrorが起きます。
そこで、TypeErrorが起きた場所で、try-except文を使って数字を文字列に変える、もしくは、文字列を数字に変えるなどの処理を行うように指定しておきます。これで型を合わせることができますので、結合または演算もできるようになります。
try-except文を使うと処理を分けることもできます。詳しい解説は書籍やチュートリアルで勉強してみてください。
- 例外処理をするときの注意点
try-except文で囲う場合は、条件を大きく囲わないことが重要です。
というのも、例外が起きるのが怖いからと言って、ひとつのtry-except文の中でいくつかの条件を例外で設定してしまうと、どこでエラーが起きているのかが判別できなくなるためです。そのため、try-except文では、問題が起きそうな場所を特定して、小さくtry-except文で囲うようにしましょう。
また、try-except文そのままでは、例外をなんでもキャッチしてしまいますので、例えばTypeErrorだけキャッチするというような書き方しましょう。こうしていかないと、予期しないエラーの時に違う処理を行ってしまいます。どうしても止められないなどのユースケースによって異なりますが、try-except文を使う時は、明確にエラーをキャッチできるように書く必要があります。
例外処理は上手く使わないと、プログラム上で何の問題が起きているのかわかりにくく、処理が止まってしまいます。例外を使って、起きている問題を途中でもみ消さないように作っていくことで、最終的には何が起きているのかを把握しやすくなります。
try-except文については公式問題集でも扱っていますし、より詳しく知りたい方は参考書籍やチュートリアルを読んでみてください。
ちなみにVS CodeなどのIDEを使っていた場合、設定を適切に行っていればエラーの改善に向けたメッセージがいろいろ出てきます。こういった機能を上手く活用するのもコーディングを進めるにあたっては重要なポイントだと思います。
参考リンク)
Python チュートリアル » 8. エラーと例外
徹底攻略Python 3 エンジニア認定[基礎試験]問題集
スラスラわかるPython第2版
- 例外の種類
では次に例外の中でもよくみられるエラーメッセージをご紹介します。
1)TypeError:
データ型に対するものです。例で挙げた数字と文字列という型の違いで起こります。
2)ValueError:
値に関するエラーです。
例えば文字列を数値に変える[int(”a”)]をしようとした際、入れられた値がaなど変換できない文字列だった場合に変換できないことによって起きるエラーです。
3)IndexError:
リストやタプルのように、インデックス操作できるものに対して出るものです。
例えば、インデックスが3までしかないのにそれより大きな値が指定された、というようなときに発生します。
4)KeyError:
辞書にKeyでアクセスする際、該当するKeyが存在しない場合に起きるエラーです。
5)AttributeError:
本来なら文字列型のデータを受け取る予定だったところに、違うデータ型が到達してきたなど、属性にアクセスしたときに起きるエラーです。
例えば文字列aに対して[”a”.upper()]とやると大文字のAが返されます。upperは文字列に宣言されているメソッドですが、これによって文字列型で宣言されている属性にアクセスして大文字のAに変えます。
一方、これを数値の1に対して行ってしまった場合、int型にはupperメソッドは定義されてないためエラーが起きます。
また、リストでは.appendというメソッドが使えますが、辞書に.appendというメソッドはありませんので、この時にもAttributeErrorが出ます。
AttributeErrorの場合、スペルや属性名が間違えていたり、違う名前やメソッドが指定されていたりします。ほとんどはスペルミスや勘違いから起きているので、落ち着いて確認してみましょう。
6)NameError:
変数名が宣言されていない時などに、名前が存在していない時に起きます。
7)UnboundLocalError:
変数自体はモジュールや関数の中で宣言されているものの、宣言より以前にその名前を呼び出してしまったことで、この段階では使えないという時に出るエラーです。
デバックなどでプログラムの順番を入れ替えた時に起きがちなので確認してみましょう。
8)ImportError:
モジュールの名称が間違っていたり、サードパーティ製ライブラリのインストールが終わっていないのにインポートしようとしたときなど、インポートができない時に起きるエラーです。
モジュールのインポートは先頭に書かれるため、ImportErrorが起きたという事は処理が進まないということになります。
動かないエラーに近いとも言えますが、例外という扱いになります。まれにImportErrorが起きても処理ができるよう、例外処理を書くことがあります。
■さいごに
Pythonにおいて独自の例外を定義することはよくあります。
上記で挙げたようなエラーを覚えておけば、大抵の例外に対して対応できるようになるはずです。
もちろん、これ以外にも例外はたくさんありますし、エラーメッセージを読んでも、どこで起きているのかわかりにくいこともありますし、見慣れないエラーが出てくれば当然怖くなります。
特にpandasのようなサードパーティ製パッケージを使っている場合には独自のエラーメッセージが出ることもあります。
こうしたサードパーティ製ライブラリで独自に定義されたエラーの場合は、ライブラリ側のエラー仕様を見ないと対処ができないことも中にはありますので、当然戸惑うこともあると思います。
ですが、いまはネットでの検索やQAサイト、ChatGPTなど調べる方法がたくさんあります。
そうして調べて、直すというのを積み重ねることで慣れてくるはずです。慣れることができれば適切な処理ができるようになります。
いろいろ試していってもらえればと思います。