[python]「AttributeError: module(object) ‘xxx’ has no attribute ‘yyy’」が起きたときの対処法5選

#はじめに

機械学習を始めとし、プログラミングに対する興味は年々高まっています。特に、先人たちが作ったモジュールを使えるPythonは、トップクラスの成長率をもっています。

しかし、Pythonを活用する上で大きな障害となるのが「AttributeError」。Python使いなら誰もが通る道でしょう。

この記事では、AttributeErrorに関する5つの原因と対処法について説明します。

・属性名のスペルミス・誤字
・ファイル名とモジュール名が同じになっている
・バージョンの違い
・属性の初期設定に問題がある
・メソッドの入力に問題がある

#AttributeErrorって何？

「AttributeError: module ‘xxx’ has no attribute ‘yyy’」を直訳すると、「属性エラー：モジュール‘xxx’ に属性‘yyy’はありません」。すなわち、存在しないメソッド(クラス内に持つ関数)を実行しようとしていることになります。

同じような意味を持つエラーで「 'xxx' object has no attribute 'yyy'」もあります。


#原因1：属性のスペルミス・誤字

　ただの誤字なんて初歩的じゃん…と侮れないのが恐ろしいところ。実際、質問サイトにある AttributeErrorの原因の1割は、このスペルミスです。特に初心者がやりがちなミスです。エラーが出たらまずはこれを疑いましょう。

以下のような簡単なコードでも、簡単にエラーが発生します。

```python
import random
attack = random.randnt(1,7)#正しくはrandint

#AttributeError: module 'random' has no attribute 'ranint'
```

対処法として、まずはメソッド名があっているかどうか確認しましょう。もしくは、一度そのコードを書き直してみましょう。人間誰しも、自身の誤字には気づきにくいものです。

コードを書き直してもエラーが続く場合、間違った名前で覚えている可能性があります。**モジュールのドキュメントを公式サイトやGithubで確認してみる**のもいいでしょう。


#原因2：ファイル名とモジュール名が同じになっている

カレントディレクトリにモジュール名と同じ名前のファイル(例えばpandas.pyなど)が有ると、そのファイルが先に読み込まれてしまい、本来読み込むべきモジュールが読み込まれなくなります。

**モジュールに関するメソッド全般が使えない**とき、この原因を考えるといいでしょう。

対処法としては、原因となるファイルの名前を変えるかファイル自体削除することです。モジュールに関するプログラミングの練習をしている場合、モジュール名をそのままファイル名にしてしまいがちなので注意しましょう。


#原因3：バージョンの違い

tensorflow・chainerなど発展途上のライブラリでは、**バージョンによってメソッド名が変わっている(消えている)**ことがあります。古いバージョンを使っている場合だけでなく、新しいバージョンを使っている場合でも起こる可能性はあります。

tensorflowのバージョン違いによるメソッド名変更については以下の記事があります。

参考記事：[TensorFlow の "AttributeError: 'module' object has no attribute 'xxxx'" エラーでつまづいてしまう人のための移行ガイド](https://qiita.com/shu223/items/ef160cbe1e9d9f57c248)

ほとんどのメソッドは使える・一部のメソッドだけ使えない場合に、この原因を想定するといいでしょう。

対処法としては、バージョンに合ったドキュメントを見て、目的のメソッドが存在するかどうか確認すること。存在しない場合、バージョンのずれがありえます。

バージョンにずれがあれば、一度モジュールをアンインストールしてから、以下のように**バージョンを指定してインストール**しましょう。

```python
pip install module==1.8.0 #moduleはモジュール名
```
モジュールのバージョンだけでなく、pythonのバージョン違いが原因になることもあります。python2を使っていた人の場合、python3では扱いの異なる部分があるので(特に文字列関連)注意しましょう。

参考記事：[Python 2.7.x と 3.x の決定的な違いを例とともに](https://postd.cc/the-key-differences-between-python-2-7-x-and-python-3-x-with-examples/) (Postd)


#原因4：属性の初期設定に問題がある(自分でクラス・メソッドを作っている場合)

以下のコードのように、selfをつけ忘れて変数定義したり、本来クラスの中に入るべきメソッド(関数)がインデントミスにより独立してしまっていると、変数やメソッドを呼び出した際にエラーが発生します。

```python
class hero():
    def __init__(self):
        HP = 30 #selfがついていない
        
def attack(self): #インデントミスでclass内に入ってない
    print('attack!')

hoge = hero()
print(hoge.HP) #AttributeErrorが出る
hoge.attack()  #これもAttributeError
```

こうしたエラーは、自分でクラスやメソッドを作っている場合に起こり得るものです。

クラスの中にクラスを作るなど複雑な設計を行っている場合、インデントミスによるエラーの確率はさらに高くなります。できるだけシンプルな設計を心がけましょう。


#原因5：メソッドの入力に問題がある

変数のオブジェクトが想定と異なる場合、AttributeError発生の原因となります。

大抵の場合、エラー箇所のコードに問題があるのではなく、それ以前の処理に問題があります。

例えば以下のようなコードだと、lst.append(1)によりリスト内の値がint型になっているため、本来str型に使うべきcapitalizeメソッドと噛み合わずエラーが発生します。

```python
lst = []
lst.append(1)              #問題の原因はここ
print(lst[0].capitalize()) #AttributeError
```

変数について、想定している型と実際の型が同じかどうか注意しましょう。


別の例で、以下のコードのように関数の戻り値を定義していない場合、自動的に戻り値がNone(NoneType型)となり、これまたエラーの原因となります。

```python
def hoge():
    moji = '文字'
    #戻り値(return)が無いためNoneを返す

moji = 'a'                 #str型のように見えるけど…
print(hoge().capitalize()) #やはりAttributeError
```

事前に値を設定しておいても、**関数の戻り値によって値が上書きされてしまう**ためエラーになります。

今回の例ならバグを見つけるのは容易ですが、他からインストールしたモジュールを使った複雑なプログラムの場合、エラーの原因を見つけるのは難しくなります。

不正な値やタイムアウトによってNoneを返すことは大いに有り得る(スクレイピング系とか)ので、**'NoneType' object has no attribute 'xxx'**を見つけたら直前の処理を疑うと良いでしょう。
 


#まとめ

AttributeErrorになりうる基本的な原因について5つ紹介しました。

AttributeErrorに限らず、**エラーメッセージはエラーの原因を突き止める上でとても重要**です。英語が苦手だと読むのも嫌になるかもしれませんが、google翻訳に突っ込んで読むだけでもいいので一度は読みましょう。

Qiitaには、以下の記事のようなエラー一覧も紹介されています。

参考記事：[Pythonエラー一覧（日本語）](https://qiita.com/soutarrr7/items/84e529d87aa3b3a9adcb)

エラーメッセージを理解し、考えうるエラーの原因や対処法を経験や書籍を通じて蓄積することが、よりよいプログラミングにつながるでしょう。