自作モジュール名に注意! AttributeError または ImportError

ライブラリからインポートするモジュール名と同じモジュール名でモジュールファイルを作成するとエラーになります。
自作モジュール名には、使用するライブラリ名と同じ名前を使わないようにしよう。

ImportError: 作成したプログラムのモジュール名がインポートするライブラリのモジュール名と同じ場合

標準ライブラリに含まれるパッケージ、datetime インポートして使うサンプルプログラムを実行しようとして次のような内容のファイルを作成するとします。
このときコードを保存するファイル名をdatetime.py という名前にします。

from datetime import datetime

today = datetime.today()
print("today:", today)
print("today.year:", today.year)
print("today.month:", today.month)
print("today.day:", today.day)

これを実行すると、次のように、ImportError となりました。

$ python datetime.py
Traceback (most recent call last):
  File "datetime.py", line 1, in <module>
    from datetime import datetime
  File "/Users/hyuki/startlab_lesson/wiki/datetime.py", line 1, in <module>
    from datetime import datetime
ImportError: cannot import name 'datetime' from partially initialized module 'datetime' (most likely due to a circular import) (/Users/hyuki/startlab_lesson/wiki/datetime.py)

部分的に初期化されたモジュール「datetime」から名前「datetime」をインポートできません(おそらく循環インポートが原因です)とメッセージが出ています。

このエラーの原因は、実行しようとしているモジュールファイルの名前 datetime.py がインポートしているモジュール名 datetime と同じであることにあります。
その理由は、モジュールのインポートをするライブラリが検索されるときに、最初にスクリプトを実行しているカレントディレクトリの検索が行われているからです。

ファイル名を datetime.py から my_datetime.py という名前に変更してみます。

$ mv datetime.py my_datetime.py
$ python my_datetime.py
today: 2022-07-03 14:24:48.730438
today.year: 2022
today.month: 7
today.day: 3

今度は正しく実行されました。(実行した日の時刻になります。)

ImportError: 作成したプログラムと同じディレクトリにインポートするライブラリと同じ名前の別のモジュールが存在する場合

次に、my_datetime.py となじディレクトリに datetime.py という名前で中身が空のファイルを作成します。

$ echo "" > datetime.py
$ ls
datetime.py    my_datetime.py

そして my_datetime.py を実行してみると、次のようなエラーが出ます。

python my_datetime.py
Traceback (most recent call last):
  File "my_datetime.py", line 1, in <module>
    from datetime import datetime
ImportError: cannot import name 'datetime' from 'datetime' (/Users/hyuki/startlab_lesson/wiki/datetime.py)

エラーメッセージは 「datetime」から「datetime」という名前をインポートできません と言っています。
エラーメッセージはさきほどとは少し違い循環インポートについてのメッセージは出ていませんが、ImportError という例外が発生している点は同じです。

共通していることは、本来標準ライブラリに含まれる datetime モジュールをインポートして使おうとしているのに、カレントディレクトリにある datetime.py が読み込まれて、標準ライブラリの datetime が読み込まれないというところです。

AttributeError: 作成したプログラムと同じディレクトリにインポートするライブラリと同じ名前の別のモジュールが存在する場合

my_datetime.py とはインポートのしかたが違う、下記のコードを my_datetime1.py に保存してください。

import datetime  # ここが違う

today = datetime.datetime.today()  # ここも違う
print("today:", today)
print("today.year:", today.year)
print("today.month:", today.month)
print("today.day:", today.day)

再び、中身空のファイル datetime.py がカレントディレクトリにある状態で my_datetime1.py を実行してみます。

$ echo "" > datetime.py
$ python my_datetime1.py
Traceback (most recent call last):
  File "my_datetime1.py", line 3, in <module>
    today = datetime.datetime.today()
AttributeError: module 'datetime' has no attribute 'datetime'

やはりエラーになりましたが、今回は、AttributeError という例外メッセージが出ました。前回は ImportError でしたので、違いがあります。今回は、インポートするときでなく、ライブラリの datetime モジュールに含まれる datetime型の属性を使用しようとしたときにエラーが発生したので、属性がないという趣旨のエラーが出たのです。

このように、インポートの方法が違うと発生するエラーも違うので、注意してください。

最後に、my_datetime1.py が正しく実行できるようにするには datetime.py をカレントディレクトリから削除しておいてください。

まとめ

自分で Python のプログラムを作成してモジュールファイルに保存するときのファイル名は、ライブラリからインポートしようとしているモジュール名またはパッケージ名と同じ名前にしないようにしましょう。

原因不明の AttributeError または ImportError が起こったときはインポートしようとしているモジュール名と同じ名前のモジュールがカレントディレクトリにある可能性があります。その場合はカレントディレクトリにあるモジュール名(ファイル名)を変えるなどして対処しましょう。

\Pythonを基礎から勉強したい方向け「プログラミング入門コースお気軽にご相談ください

無料カウンセリングを予約する