どうも、きっしゅです。
Pythonに限らす実装時に直面するのがエラーです。試しに動かしてみようとしたけど、エラーが発生してしまった。すぐに原因がわかればいいのですが、すぐにわからない場合もあります。
自分がPython初心者のころはエラー原因の特定にすごく時間がかかっていました。
そこで今回は実装時の動作確認の際に発生しがちなエラーとその原因、解決策をまとめました。
開発時に発生しがちなエラーとその解決方法
PythonではBaseExceptionを筆頭に多くのエラークラスが定義されています。
頻繁に直面するエラーもあれば、滅多にお目にかかることがないエラーも存在します。
一覧表を作成しようとしたのですが、そこそこ数が多いので一覧の記載はやめておきます。
もし興味がありましたら、詳細は公式に記載されていますので是非ご覧ください。
開発時に頻繁に直面するエラーとその原因
まずは、頻繁に直面するエラーとその原因の一覧を作成しました。
各エラーの詳細は記事の後半に記載していきます。
エラー名 | 原因 |
---|---|
AttributeError | 参照がおかしいときに発生します。多くの場合は属性のタイポ(変数等のタイピングミス)か、Noneに対して不可能な処理をしようとしていることが原因です。 本当に正しく変数名を宣言して、宣言した変数を使おうとしているか確認してください。 |
ImportError | 使おうとしているモジュールのImportに失敗した場合に発生します。多くの場合は開発環境にそのモジュールがインストールされていない、もしくはImportしようとしているモジュールのパスがおかしいことが原因です。 Import文をもう一度見返してください。 |
IndexError | List型のデータからインデックス指定をして値を取り出す際に、存在しないインデックスを指定すると発生するエラーです。 単純にインデックスの指定が間違っている、もしくは想定した数の要素がListの中に入っていないことが大半です。 デバッグをしてどのような値が入ってきているかを確認してください。 |
IndentationError | インデントがおかしい場合に発生します。 エラー発生箇所のインデントが正しいか否かを確認してください。 |
KeyError | Dict型のデータからKeyを指定して値を取り出そうとした際に存在しないKeyを指定した際に発生するエラーです。 こちらもIndexErrorと似た感じで、Keyの指定が間違っている、もしくは想定した値がDictの中に入っていないことが大半です。 デバッグをしてどのような値が入ってきているかを確認してください。 |
NameError | 宣言されていない変数を使おうとした際に発生するエラーです。 多くの場合はタイポです。スペルが間違っていないかを中心に本当に宣言した変数、関数であるかを確認してください。 |
SyntaxError | 構文がおかしい場合に発生するエラーです。 構文エラーは原因は多数ありますが、経験上、多くの場合はif文やfor文の後ろの:コロンが抜けています。 構文が正しいかどうかの確認をしてください。 |
TypeError | データ型がおかしい場合に発生します。 経験上は演算の際にInt型とStr型を足し算しようとして発生すること、もしくは関数への引数の数が間違っている場合に発生することが多いです。 デバッグやtype(変数)を使ってどのようなデータ型の値が入ってきているかを確認、もしくは呼び出し先の引数の確認をしてください。 |
ValueError | 想定外の値が入ってきた場合に発生します。 あらゆるパターンで発生する可能性があります。 個人的な経験では多くの場合は、str型をdatetime型に変換する際にフォーマットが意図したものと違うことによって発生することが多いです。 デバッグをしてどのようなデータが入ってきているかを確認してください。 例:datetime.datetime.strptime(‘2020/07/01’, ‘%Y%m-%d’) 年月日の繋ぎが期待したフォーマットでは–(ハイフン)だったが、実際に入ってきたStringは/(スラッシュ)だった。 |
各エラーの発生例と解決案
AttributeError
AttributeErrorが発生しらたまず1番に疑いたいのがスペルミス等のタイポです。
例をあげると以下のような場合に発生します。
上手の例ではnowのスペルを間違えてnawにしたことによってAttributeErrorが発生しています。
次に疑いたいのが変数にNoneが入っている可能です。
例をあげると以下のような場合に発生します。
Noneが代入された変数に対して、全て大文字に変換する処理をしようとしてAttributeErrorが発生しています。
ただ実際の実装ではNoneを宣言して、そのまま使う上図のようなミスは少ないと思います。
ありがちな実装ミスを使って例をあげると以下のようになります。
返り値があることを想定している関数でreturn文を書くのを忘れた際によく発生します。
Pythonの場合、関数がreturnを返さない場合、呼び出しもとの変数にはNoneが入る仕様になっています。それ故に上記の画像のようにNoneが変数に入ってきてAttributeErrorを発生させてしまっています。
returnの書き忘れは意外と発生するので、タイピングミスではなかった場合はNoneが入っていないかの確認をするといいと思います。
ImportError
このエラーは非常にシンプルでモジュールをインポートする際に存在しないモジュールをインポートしようとして発生するエラーです。
モジュール名のタイピングミス、モジュールのパスのミスなどが主な原因です。
例をあげると以下のような場合に発生します。
まずはタイピングミスによるエラーの場合。
datetimeの最後のeが抜けているためにエラーが発生しています。
続いては、パスの指定がおかしい場合です。
相対パスで指定しようとして、実際にファイルが存在するディレクトリとは異なる階層を参照したが故に、ファイルが存在せずImportErrorが発生しています。
ちなみに開発環境にインストールされていないモジュールをインポートしようとするとModuleNotFoundErrorが発生します。ModuleNotFoundErrorはImportErrorのサブクラスになります。
例をあげると以下のようになります。
これはモジュールをインストールする必要があります。
個人で開発する分には自由にインストールを実施したらいいと思います。
ですが、プロジェクトで開発を行っている場合などは個人で自由にインストールはできないと思います。大抵の場合は実装している環境、リリース予定の環境(プログラムが動くサーバー)に同じモジュールを追加でインストールする必要があるのではないでしょうか?
モジュールをインストールするのは非常に簡単ですが、そのモジュールのバージョンに釣られて依存関係のある既存のモジュールのバージョンが上がる可能性もあります。
これが原因で既存のプログラムが動かなくなる可能性も大いにあり得るので、個人で解決するのではなく所属するプロジェクトの有識者に確認することをお勧めします。
IndexError
このエラーはList型のデータからインデックスを指定して値を取り出す際に、存在しないインデックスを指定すると発生します。
この場合はどんなデータが入ってきているかを確認するためにデバッグするしかないと言っても過言ではないと思います。
例をあげると以下ようになります。
プログラミングに慣れてくるとList型からインデックスを固定で数字を入力して取り出す実装はほとんどしなくなると思います。実際自分はほぼ使いません。
ですが、初心者の方は比較的陥りやすいエラーだと思いますので、IndexErrorが発生した際はデータを疑うようにすればいいと思います。
defaultlistを使えばこのようなエラーは発生しません。ですがdefaultlistを使いこなすのは少々スキルが必要ですし、通常のListとは異なるリスクもあります。
詳細はこの場では触れないで、別の機会に記事を書こうと思います。
IndentationError
このエラーはインデントがズレている場合に発生します。
Python特有のエラーなのではないでしょうか?
Pythonはインデントでスコープを制御します。一方で多くの言語は括弧((), {}, [])で括ってスコープを制御します。
インデントがズレると言うことは、括弧が足りないこと、もしくは多いことと同義だと考えても問題ないと思います。
シンプルな例をあげると以下のような場合があります。
また、よくやりがちなのがfor文やif文を書いた際にインデントがズレてしまうことです。
以下の画像のようなパターンですね。
特にfor文でインデントがズレると無限ループになったり想定以上に処理が走ったりするのでエラーが発生していなくてもインデントには要注意です。
KeyError
これはDict型のデータから値を取り出す際に、存在しないKeyを指定した場合に発生するエラーです。
IndexErrorと同様でどんなデータが入ってきているかを確認するためにデバッグするしかないと言っても過言ではないと思います。
例をあげると以下のような場合があります。
自分が実装する範囲では想定しているKeyがないことは実装ミス意外では滅多にないと思います。
ですが、ファイルの処理、Webスクレイピングなど外部からデータを取得する場合は想定しているKeyがない場合は珍しくはないです。
そんなときに役に立つのがget関数です。これは指定したKeyがない場合はエラーを発生させずNoneを返してくれます。
上図を見てわかるように、通常のKey指定ではエラーが発生していましたが、getを使うことでエラーが発生せず、Noneが入ってきていることがわかります。
またget関数は第二引数を渡すことでデフォルトの返り値を指定することができます。
下図の例だとKeyが存在しない場合はDefault valueという文字列を返すようにしています。
このように実装することで、Keyが存在しなかったとしても任意のデータ型の値を返すことができるので後続の処理への影響をなくすことができます。
KeyErrorの解決策と同時にget関数も覚えておくことをお勧めします。
NameError
こちらのエラーは宣言していない変数を使おうとすると発生します。
多くの場合はスペルミスによって発生していることがおおいので、まずはスペルミスを疑ってソースコードを確認することをお勧めします。
例を挙げると以下のようになります。
SyntaxError
こちらのエラーは構文エラーです。Pythonでは最も直面するエラーのうちの一つだと思います。
原因は様々です。多いのはfor文、if文で条件式を書いた後の:(コロン)を忘れたことが原因であることが多いと思います。
例をあげると下図になります。
他にはimport分の記載がおかしい場合や、文字列をくくるためのクォーテーションマークを忘れた際にも発生します。
ただクォーテーションマークを忘れた場合、大抵はエディターの文字色がおかしいと感じるので、コードの実行前には気づくと思います。
Import文がおかしい場合。
また意外とハマりがちなのが、全角のスペースがソースコード内で使われてしまっている場合です。
文字列として全角スペースを使うことは可能ですが、それ以外の場所では一切使うことができません。
全角スペースはエディター上では意外と気付きにくかったりするので、なかなか原因が特定できずに時間を費やしてしまうことも珍しくはありません。
少しわかりにくいですが、赤枠の部分に全角のスペースを入力しています。
今回は行の先頭のに入力しましたが、ソースコードの途中や行の終わりに入力されていた場合にも同様のエラーが発生します。
ひたすらに全角スペースを探す必要があります。エディターによっては文字列意外での全角スペース入力を許可しない設定ができるものもあるそうなので、その設定を入れておくことをお勧めします。
TypeError
これはデータに対して実行しようとした操作が、そのデータ型ではサポートされていない場合に発生します。
多くの場合は演算の際や関数へ渡す引数の数が間違っている場合に発生する可能性が高いです。
Pythonが初めてのプログラミング言語の人は型という概念に最初苦しむかもしれません。
Pythonは変数に対して明示的に型の宣言をする必要がなく、他の言語に比べると型を気にする機会が少ないです。
それ故にTypeErrorが出るとなにが起きたのか全く分からずに困ってしまいます。
基本的には異なったデータ型を演算子を使った結合等の処理を実施することはできません。データ型を揃えてあげる必要があります。
例をあげると以下のような感じです。
上図の場合、文字列(str型)と数字(int型)を足し算しようとしてエラーが発生しています。
データ型が異なることが原因です。
解決するためにはデータ型を揃える必要があります。
今回の場合だと下図のようにすることで解決します。
演算でTypeErrorが発生した場合はまずは全てのデータ型が統一されているかをまず1番に疑うことをお勧めします。
続いて多いのは、関数に渡す引数の数が間違っている場合です。
例をあげるといかのような場合があります。
他にTypeErrorが発生するのはデータ型がサポートしていない操作をしようとした場合です。
例をあげると以下のような場合があります。
この場合は想定外のデータが入ってきている可能性が高いのでデバッグして値を確認するしかないと思います。
ValueError
TypeErrorとは異なりデータ型は合っているが、データの中身が適切でない場合に発生するエラーです。
例をあげると以下のような感じです。
ここの処理ではstr型をint型に変換しようとしています。その処理自体は問題なく実行可能です。
ですが、int型(整数)に変換するということは当然ですが、データも数字である必要があります。上手の例の場合も、変数に数字の文字列が入っている場合は正常に処理が実行され、文字列に数字以外の文字が含まれている場合はエラーが発生しています。
個人的な経験ではValueErrorが一番多く発生するのは、文字列からdatetime型に変換する際です。文字列からdatetime型への変換の際には日付のフォーマットを指定する必要があります。
実際にデータとして受け取った文字列の日付のフォーマットと、変換の際に指定したフォーマットが異なっているとValueErrorが発生します。
例をあげると以下のような場合です。
日付フォーマットの違いによるValueErrorは割と頻繁に直面します。年月日の繋ぎが/(スラッシュ)なのか–(ハイフン)なのか、時分秒はミリ秒(少数以下)を含むのか含まないのか、それだけでもずれるとエラーが発生します。
アップロードしてもらったファイルや外部のサービスからデータを取得して処理する場合に意外と発生しがちです。
ValueErrorが発生した時はデータの中身を疑ってデバッグすることをお勧めします。
まとめ
いかがでしたでしょうか?
どんなに優秀なプログラマでも最初から最後まで一回もエラーに直面することなく実装できることは滅多にないと思います。
プログラミングをする以上はエラーから逃れることは難しいと言っても過言ではないと思います。そう考えるといかにしてエラーを素早く対処するかはプログラマとしては重要な要素になると思います。
趣味、仕事、研究などなどみなさまの快適なPythonライフの役に立ちますと幸いです。
コメント