HOWTO · Python

Python の ImportError: cannot import name を修正する方法

名前、モジュールパス、シャドーイング、バージョン、循環インポートを確認して Python の ImportError: cannot import name を診断し、範囲を絞った2モジュールの修正に従います。

このページの内容

ImportError: cannot import name は、Python がモジュールを見つけたものの、要求された名前をそのモジュールから取得できなかったことを意味します。循環インポートは原因の一つです。Python がモジュールを初期化している間に、別のモジュールがまだバインドされていない名前を要求することがあります。スペルミス、想定とは異なるモジュール、ローカルファイルによるシャドーイング、利用できない依存関係、パッケージや API のバージョンの不一致でも似た症状が起きます。(demo1_func1; demo2_func1; demo2_func2)

Python の ImportError: cannot import name エラーの原因

demo1.py と demo2.py を考えてみましょう。各ファイルがモジュールレベルで from other_module import function を使うと、どちらのファイルをインポートしても、初期化が完了する前にもう一方のインポートが始まります。特に、2番目のモジュールが要求した時点で、その関数がまだ存在しないことがあります。Python はインポートの一部としてモジュールレベルの文を実行します。関数が後から現れるのを待つことはありません。

トレースバックとインポートのコンテキストを使って原因を絞り込みます。

  1. トレースバックをすべて読み、Python がインポートできなかったモジュールと名前を特定します。
  2. スペルと大文字・小文字が、そのモジュールの定義と一致しているか確認します。
  3. Python が解決するファイルを確認します。標準ライブラリのモジュールや依存関係と同じ名前の近くのファイルが、本来のモジュールをシャドーイングすることがあります。
  4. パッケージがアクティブな環境にインストールされていて、そのバージョンがコードの想定する API を提供しているか確認します。
  5. 名前とパスが正しい場合は、両方向のモジュールレベルのインポートをたどって循環を見つけます。

名前の後に続く正確な文言は Python のバージョンとインポートのコンテキストによって異なるため、固定の接尾辞に頼らず、トレースバックとモジュールのコードから診断してください。

Python の ImportError: cannot import name エラーの修正

この2モジュールのケースでは、モジュールをインポートし、関数の実行時にその属性を参照します。これにより、名前の検索は両モジュールのインポートが完了するまで遅延されます。次に示す自己完結型の例は、一時ディレクトリに demo1.py と demo2.py を作成し、demo2.py を実行します。

from pathlib import Path
import subprocess
import sys
import tempfile

demo1_source = '''import demo2

def demo1_func1():
    print("demo1_func1")
    demo2.demo2_func2()
'''

demo2_source = '''import demo1

def demo2_func1():
    print("demo2_func1")
    demo1.demo1_func1()

def demo2_func2():
    print("demo2_func2")

if __name__ == "__main__":
    demo2_func1()
'''

with tempfile.TemporaryDirectory() as directory:
    Path(directory, "demo1.py").write_text(demo1_source, encoding="utf-8")
    Path(directory, "demo2.py").write_text(demo2_source, encoding="utf-8")
    result = subprocess.run(
        [sys.executable, "demo2.py"],
        cwd=directory,
        text=True,
        capture_output=True,
        check=True,
    )
    print(result.stdout, end="")
demo2_func1
demo1_func1
demo2_func2

モジュールインポートへの変更は、示された循環に対処するもので、普遍的な修正ではありません。依存関係の構造によっては、共有関数を3つ目のモジュールに移動したり、インポートを関数内に移動したりする方が明確な設計になることがあります。後者はモジュールスコープで依存関係が見えにくくなります。Blueprint を使う Flask アプリケーションでは、アプリケーションのインポートを整理する際に Flask Blueprint ガイド を確認することも役立ちます。