Pythonの import とは、別のファイル(モジュール)やフォルダ(パッケージ)に書かれた関数・クラス・変数を、自分のコードから使えるようにする文です。 標準ライブラリも、pip で入れたライブラリも、自分で書いたファイルも、同じ import 文で読み込みます。

書き方そのものは短いのですが、自作のファイルを読み込もうとすると ModuleNotFoundError や attempted relative import に出会うことがよくあります。原因のほとんどは「Python がどのフォルダを探しているか(sys.path)」と「どうやって実行したか」の 2 点に集約されます。

この記事では、import 構文の一覧表、フォルダ構成図つきの自作モジュール・パッケージの作り方、そして代表的なエラーの再現と原因別の解決手順を、すべて手元で実行した結果つきで紹介します。検証は Python 3.11.15、一部のエラーメッセージの比較に Python 3.14.7 を使いました。出力中のパスは /home/user/... に置き換えています。

importの早見表

構文の一覧

書き方 意味 使うときの書き方
import calc モジュール全体を読み込む calc.add_tax(100)
import calc as c 別名を付けて読み込む c.add_tax(100)
from calc import add_tax 中の名前を直接読み込む add_tax(100)
from calc import add_tax, TAX_RATE 複数の名前を読み込む add_tax(100)、TAX_RATE
from calc import add_tax as tax 名前に別名を付ける tax(100)
from calc import * 公開されている名前をすべて読み込む add_tax(100)(由来が見えにくくなる)
import app.utils.money パッケージ内のモジュールを読み込む app.utils.money.add_tax(100)
from app.utils import money パッケージからモジュールを読み込む money.add_tax(100)
from . import item 相対 import:同じパッケージ内のモジュール item.Item(...)
from .item import Item 相対 import:同じフォルダのモジュールの中身 Item(...)
from ..utils.money import add_tax 相対 import:1 つ上のパッケージから add_tax(100)
importlib.import_module("app.utils.money") 文字列で指定して読み込む 戻り値のモジュールを使う

エラーから原因を引く表

エラー文(一部) 主な原因 まず試すこと
ModuleNotFoundError: No module named 'app' 探す場所(sys.path)にそのフォルダがない ルートのフォルダで python -m を使って実行
ModuleNotFoundError: No module named 'requests' その Python にインストールされていない python -m pip install requests
ImportError: attempted relative import with no known parent package 相対 import を含むファイルを直接実行した python -m パッケージ.モジュール で実行
ImportError: attempted relative import beyond top-level package .. がパッケージの最上位を越えた ドットの数、実行する場所を見直す
ImportError: cannot import name 'X' from partially initialized module 循環 import import を関数の中へ移す、共通部分を別ファイルへ
ImportError: cannot import name 'X' from 'calc' 名前のつづり違い、その名前が定義されていない モジュール側の定義を確認
AttributeError: module 'random' has no attribute ... 標準ライブラリと同名の自作ファイルがある 自作ファイルの名前を変える

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Python importの使い方|自作モジュールとエラー対処」

基本の使い方:自作モジュールを import する

モジュールとは、.py ファイル 1 つのことです。 ファイル名から .py を取ったものがモジュール名になります。まずは同じフォルダに 2 つのファイルを置く、いちばん単純な形から始めます。

shop/
├── calc.py     ← 自作モジュール
└── main.py     ← 実行するファイル

calc.py:

TAX_RATE = 0.1

def add_tax(price):
    return int(price * (1 + TAX_RATE))

def _round(x):
    return round(x)

if __name__ == "__main__":
    print("calc.py を直接実行:", add_tax(1000))

main.py:

import calc
import calc as c
from calc import add_tax
from calc import add_tax as tax, TAX_RATE

print(calc.add_tax(1000))
print(c.TAX_RATE)
print(add_tax(200), tax(300), TAX_RATE)
print(calc.__name__, __name__)
cd shop
python3 main.py

実行結果:

1100
0.1
220 330 0.1
calc __main__

4 つの書き方はどれも同じ calc.py を読み込んでいます。違いは「どの名前で使えるようになるか」だけです。

import と from の使い分け

書き方 長所 気を付ける点 向いている場面
import calc calc.add_tax と書くので、どこの関数か一目でわかる 毎回モジュール名を書く 基本はこれ
import numpy as np 長い名前を短くできる 独自の略し方は読み手が迷う 慣習の別名があるライブラリ
from calc import add_tax 短く書ける 同じ名前の変数を作ると上書きされる 何度も使う少数の名前
from calc import * 一度に全部使える どの名前がどこから来たか追いにくい 対話モードでの試し書き

迷ったら import モジュール名、頻繁に使うものだけ from ... import 名前 とすると、読みやすさと書きやすさのバランスが取れます。

import は最初の 1 回だけ実行される

同じモジュールを何度 import しても、ファイルの中身が実行されるのは最初の 1 回だけです。読み込んだモジュールは sys.modules という辞書に記録され、2 回目以降はそこから取り出されます。

import calc, sys
import calc
print(calc.__name__, 'calc' in sys.modules, calc.__file__)
calc True /home/user/shop/calc.py

モジュール.__file__ を表示すると、実際にどのファイルが読み込まれたかがわかります。 意図しないファイルを読んでいないか調べるときの、いちばん確実な方法です。対話モードでファイルを書き換えたあとに読み直したいときは importlib.reload(calc) を使います。

if __name__ == "__main__" の意味

先ほどの出力の最後の行は calc __main__ でした。__name__ は、そのファイルが「どう読み込まれたか」を表す変数です。

読み込まれ方 __name__ の値
python3 calc.py と直接実行された "__main__"
import calc で読み込まれた "calc"(モジュール名)
python3 -m app.cli で実行された "__main__"

この仕組みを使うと、「直接実行されたときだけ動かす処理」を書けます。

python3 calc.py
calc.py を直接実行: 1100

main.py から import したときには、この行は表示されませんでした。import される側のファイルでは、関数とクラスの定義だけをトップレベルに置き、動作確認や起動の処理は if __name__ == "__main__": の中に入れると、import しただけで処理が走ることを防げます。

パッケージを作る:フォルダで整理する

ファイルが増えてきたら、フォルダに分けて整理します。モジュールを入れたフォルダを「パッケージ」と呼びます。

shop/                      ← ルート(ここで実行する)
├── run.py
├── app/
│   ├── __init__.py
│   ├── cli.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── item.py
│   │   └── order.py
│   └── utils/
│       ├── __init__.py
│       └── money.py
└── scripts/
    └── report.py

app/__init__.py(動きを見るために print を入れています):

print("app/__init__.py が実行されました")

app/utils/money.py(app/utils/__init__.py は空のファイル):

def add_tax(price, rate=0.1):
    return int(price * (1 + rate))

app/models/item.py:

class Item:
    def __init__(self, name, price):
        self.name = name
        self.price = price

app/models/order.py(相対 import を使用):

from .item import Item
from ..utils.money import add_tax


class Order:
    def __init__(self):
        self.items: list[Item] = []

    def add(self, item: Item):
        self.items.append(item)

    def total(self):
        return add_tax(sum(i.price for i in self.items))

app/models/__init__.py:

from .item import Item
from .order import Order

__all__ = ["Item", "Order"]

run.py:

from app.models import Item, Order

order = Order()
order.add(Item("ノート", 300))
order.add(Item("ペン", 120))
print("合計:", order.total())
cd shop
python3 run.py
app/__init__.py が実行されました
合計: 462

__init__.py の役割

__init__.py は、そのフォルダが import されたときに最初に実行されるファイルです。 上の結果でも、from app.models import ... と書いただけで app/__init__.py が実行されています。

役割 例
フォルダを通常のパッケージとして示す 空のファイルを置く
中の名前を引き上げて、短く import できるようにする from .item import Item と書くと from app.models import Item で使える
from パッケージ import * の対象を決める __all__ = ["Item", "Order"]
パッケージの初期化 バージョン番号の定義など(重い処理は入れないのがおすすめ)

app/models/__init__.py で Item と Order を引き上げているので、使う側は from app.models.item import Item と奥まで書かずに、from app.models import Item と短く書けます。

絶対 import と相対 import

パッケージの中から別のモジュールを読み込む書き方は 2 種類あります。

種類 書き方(app/models/order.py の中で) 特徴
絶対 import from app.models.item import Item ルートからの完全な名前。場所がひと目でわかる
相対 import from .item import Item 自分の位置からの相対。パッケージ名を変えても書き換え不要

相対 import のドットは、フォルダの . .. と同じ感覚で読めます。

書き方 指す場所(app/models/order.py から見て)
from . import item 同じパッケージ app/models/ の item.py
from .item import Item 同じパッケージの item.py の中の Item
from .. import utils 1 つ上のパッケージ app/ の utils
from ..utils.money import add_tax app/utils/money.py の中の add_tax

相対 import は from で始まる形だけで、import .item とは書けません。PEP 8 では絶対 import を基本とし、パッケージ内部の込み入った構成では相対 import も使ってよい、としています。チームでどちらかに決めておくと読みやすくなります。

__init__.py がないフォルダ:名前空間パッケージ

Python 3.3 以降は、__init__.py がないフォルダも import できます。これを名前空間パッケージと呼びます。手元で、__init__.py のない nsdemo/ フォルダと、ある app/ フォルダを比べました。

import nsdemo, nsdemo.mod
print(nsdemo, nsdemo.mod.X)
import app
print(app)
<module 'nsdemo' (<_frozen_importlib_external.NamespaceLoader object at 0x7f7cab70ce50>)> 1
app/__init__.py が実行されました
<module 'app' from '/home/user/shop/app/__init__.py'>

名前空間パッケージは、別々の場所にある同じ名前のフォルダを 1 つのパッケージとして合成できるのが本来の用途です。part1/acme/tools/ と part2/acme/docs/ を両方 sys.path に入れて確認しました。

import acme, acme.tools, acme.docs
print(acme.tools.NAME, acme.docs.NAME)
print(acme.__file__)
print(acme.__path__)
tools docs
None
_NamespacePath(['/home/user/ns/part1/acme', '/home/user/ns/part2/acme'])
項目 通常のパッケージ 名前空間パッケージ
__init__.py あり なし
__file__ __init__.py のパス None
初期化の処理 書ける 書けない
複数の場所に分割 できない できる
向いている場面 通常のアプリ・ライブラリ 複数の配布物で 1 つの名前を共有する大きなライブラリ

ふつうのアプリでは、__init__.py を置いて通常のパッケージにするのがおすすめです。 pytest や mypy、パッケージ化のツールがフォルダを見つけやすくなり、置き忘れによる挙動の違いに悩まずに済みます。

from module import * と __all__

from モジュール import * で読み込まれる名前は、次のルールで決まります。

モジュールの状態 読み込まれる名前
__all__ がない アンダースコア(_)で始まらない名前すべて
__all__ がある __all__ のリストに書かれた名前だけ

calc.py(__all__ なし)で確かめると、_round は除かれました。

from calc import *
print([n for n in dir() if not n.startswith('__')])
['TAX_RATE', 'add_tax']

次に、__all__ を定義した shapes.py です。

__all__ = ["area"]

PI = 3.14159


def area(r):
    return PI * r * r


def perimeter(r):
    return 2 * PI * r
from shapes import *
print(area(1))
print('perimeter' in dir(), 'PI' in dir())
from shapes import perimeter
print(perimeter(1))
3.14159
False False
6.28318

__all__ は import * の対象を絞るだけで、名前を指定した import は制限しません。 「このモジュールが外向けに公開している名前の一覧」を示す役割もあり、エディタの補完や静的解析ツールもこの一覧を参照します。なお、import * を関数の中に書くと SyntaxError: import * only allowed at module level になります。

Python はどこを探すのか:sys.path

import のエラーを理解するいちばんの近道は、sys.path(モジュールを探すフォルダのリスト) を知ることです。Python は import 文に出会うと、このリストを先頭から順に探し、最初に見つかったものを読み込みます。

showpath.py:

import sys
for p in sys.path:
    print(repr(p))
cd shop
python3 showpath.py
'/home/user/shop'
'/usr/lib/python311.zip'
'/usr/lib/python3.11'
'/usr/lib/python3.11/lib-dynload'
'/root/.local/lib/python3.11/site-packages'
'/usr/local/lib/python3.11/dist-packages'
'/usr/lib/python3/dist-packages'
順番 中身 由来
先頭 実行したスクリプトがあるフォルダ 実行方法で決まる
次 環境変数 PYTHONPATH のフォルダ 設定したときだけ
その次 標準ライブラリ Python 本体
最後 site-packages(pip で入れたライブラリ) venv ならその venv の中

大事なのは先頭の 1 行が、実行方法によって変わることです。

実行方法 sys.path の先頭
python3 scripts/report.py スクリプトのあるフォルダ(shop/scripts)
python3 -m scripts.report コマンドを実行したフォルダ(カレントディレクトリ)
python3 -c "..."・対話モード 空文字 ''(カレントディレクトリの意味)

「カレントディレクトリが探される」と思いがちですが、ファイルを直接実行したときに探されるのは、そのファイルが置かれたフォルダです。 この違いが、次の章のエラーの原因になります。

エラーの再現と解決

ModuleNotFoundError: No module named 'app'(実行場所と sys.path)

scripts/report.py から app パッケージを使おうとした例です。

import sys
print("sys.path[0] =", sys.path[0])
from app.models import Item
print(Item("消しゴム", 80).name)
cd shop
python3 scripts/report.py
sys.path[0] = /home/user/shop/scripts
Traceback (most recent call last):
  File "/home/user/shop/scripts/report.py", line 3, in <module>
    from app.models import Item
ModuleNotFoundError: No module named 'app'

shop/ で実行しているのに、app が見つかりません。sys.path の先頭が shop/scripts になっていて、その中に app フォルダがないからです。解決の方法は 3 つあり、どれも手元で動作を確認しました。

解決 1:python -m で実行する

cd shop
python3 -m scripts.report
sys.path[0] = /home/user/shop
app/__init__.py が実行されました
消しゴム

-m は「モジュール名を指定して実行する」オプションです。カレントディレクトリが sys.path の先頭に入るので、ルートのフォルダで実行すれば app が見つかります。指定はファイルのパスではなく、.py を付けないドット区切りの名前です。

解決 2:PYTHONPATH でルートを追加する

cd shop
PYTHONPATH=. python3 scripts/report.py
sys.path[0] = /home/user/shop/scripts
app/__init__.py が実行されました
消しゴム

Windows の PowerShell では $env:PYTHONPATH = "." を先に実行します。

解決 3:pip install -e . でパッケージとして登録する

ルートに pyproject.toml を置き、編集可能インストールをすると、どのフォルダから実行しても import できるようになります。

[build-system]
requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta"

[project]
name = "shop-app"
version = "0.1.0"
requires-python = ">=3.10"

[tool.setuptools.packages.find]
include = ["app*"]
cd shop
python -m pip install -e .      # venv を有効化した状態で
cd /tmp
python /home/user/shop/scripts/report.py
sys.path[0] = /home/user/shop/scripts
app/__init__.py が実行されました
消しゴム

pip list には shop-app 0.1.0 /home/user/shop と、元のフォルダの場所つきで表示されました。pip のコマンドの詳細は別記事「pipコマンド一覧」にまとめています。

方法 手軽さ 効果の範囲 向いている場面
python -m で実行 設定なし そのコマンドだけ まず試す。日常の実行
PYTHONPATH 環境変数を設定 そのシェル・その設定 Docker や CI の設定
pip install -e . pyproject.toml が必要 その venv 全体 テストやツールからも使う、長く育てるコード
sys.path.append(...) をコードに書く 1 行で済む そのファイル 使い捨ての検証(コードが環境に依存するため、常用は上の 3 つがおすすめ)

ImportError: attempted relative import with no known parent package

相対 import を含むファイルを、直接実行したときのエラーです。app/cli.py を用意しました。

from .models import Item, Order


def main():
    order = Order()
    order.add(Item("付箋", 200))
    print("合計:", order.total())


if __name__ == "__main__":
    main()
cd shop
python3 app/cli.py
Traceback (most recent call last):
  File "/home/user/shop/app/cli.py", line 1, in <module>
    from .models import Item, Order
ImportError: attempted relative import with no known parent package

直接実行されたファイルは __main__ という名前の単独のモジュールとして扱われ、「自分がどのパッケージに属しているか」の情報を持ちません。 そのため、.(自分のパッケージ)を解決できずに止まります。

解決は、ルートのフォルダから -m で実行することです。

cd shop
python3 -m app.cli
app/__init__.py が実行されました
合計: 220
直す前 直した後
python3 app/cli.py python3 -m app.cli
cd app && python3 cli.py cd shop && python3 -m app.cli

-m で実行すると、Python は app.cli がパッケージ app の一員であることを把握するので、相対 import が解決されます。VS Code などのエディタの実行ボタンは「ファイルを直接実行」する設定になっていることが多いので、実行構成で「モジュールとして実行」を選ぶと同じ結果になります。

似たエラーに attempted relative import beyond top-level package があります。これは、.. の数が多く、パッケージの最上位を越えたときに出ます。app/utils/ の中のファイルに from ....x import y と書いて import すると、手元でも次のように表示されました。

ImportError: attempted relative import beyond top-level package

ドットの数を数え直すか、絶対 import(from app.xxx import ...)に書き換えると解決します。

circular import(循環 import)

2 つのモジュールがお互いを import し合うと起こります。

member.py:

from team import Team


class Member:
    def __init__(self, name):
        self.name = name

    def team(self):
        return Team("開発")

team.py:

from member import Member


class Team:
    def __init__(self, name):
        self.name = name
        self.members = [Member("佐藤")]
python3 -c "import member"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/user/circ/member.py", line 1, in <module>
    from team import Team
  File "/home/user/circ/team.py", line 1, in <module>
    from member import Member
ImportError: cannot import name 'Member' from partially initialized module 'member' (most likely due to a circular import) (/home/user/circ/member.py)

起きていることを順に追うと、次のとおりです。

sequenceDiagram
    participant P as Python
    participant M as member.py
    participant T as team.py
    P->>M: import member(1行目を実行)
    M->>T: from team import Team
    T->>M: from member import Member
    Note over M: まだ1行目の途中。Member は未定義
    M-->>T: ImportError(partially initialized)

partially initialized module(初期化の途中のモジュール)という言葉が、循環 import の目印です。 import team の形で読み込んで属性を使った場合は、AttributeError: partially initialized module 'member' has no attribute 'DEFAULT' (most likely due to a circular import) という形で現れました。

解決方法は次の 3 つです。

方法 内容 向いている場面
import を関数の中へ移す 使う時点で import する 片方が、もう片方を一部の処理でしか使わない
共通部分を別ファイルへ出す 両方が使うものを第 3 のモジュールに移す 根本的に整理したい
型ヒント用の import を分ける if TYPE_CHECKING: の中で import 型注釈のためだけに必要なとき

1 つ目の方法で member.py を直しました。

class Member:
    def __init__(self, name):
        self.name = name

    def team(self):
        from team import Team   # 使う時点で import する
        return Team("開発")
from member import Member
t = Member('鈴木').team()
print(t.name, [m.name for m in t.members])
開発 ['佐藤']

型ヒントのためだけに相手のクラスが必要な場合は、次の書き方が使えます(標準ライブラリの typing の機能で、公式ドキュメントで確認した書き方です)。

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from team import Team   # 型チェックのときだけ読み込まれる

循環が何度も起こるときは、「2 つのモジュールの役割が重なっている」というサインでもあります。依存の向きを一方通行にする(たとえば models は utils を使うが、utils は models を使わない)と決めておくと、構成がすっきりします。

ImportError: cannot import name

モジュールは見つかったものの、その中に指定した名前がないときのエラーです。

python3 -c "from calc import add_taxx"
ImportError: cannot import name 'add_taxx' from 'calc' (/home/user/shop/calc.py)

Python 3.14.7 では、候補も表示されました。

ImportError: cannot import name 'add_taxx' from 'calc' (/home/user/shop/calc.py). Did you mean: 'add_tax'?

エラー文の末尾のかっこ内に、実際に読み込まれたファイルのパスが出ます。 ここが意図したファイルかどうかを最初に見ます。

原因 見分け方 対処
名前のつづり違い モジュール側の定義と見比べる つづりを直す
別のファイルを読んでいる かっこ内のパスが想定と違う 同名ファイルを改名する
ライブラリの版が違う その名前が追加・移動された版か確認 pip show で版を確認し、合わせる
循環 import partially initialized と書かれている 前の項の方法で解消

標準ライブラリと同名のファイル(random.py)

自作のファイルに random.py、json.py、test.py、email.py など、標準ライブラリと同じ名前を付けると、sys.path の先頭にある自作ファイルのほうが先に見つかり、標準ライブラリが読み込まれなくなります。

random.py:

import random

print(random.randint(1, 6))
python3 random.py

Python 3.11.15 の結果:

Traceback (most recent call last):
  File "/home/user/shadow/random.py", line 1, in <module>
    import random
  File "/home/user/shadow/random.py", line 3, in <module>
    print(random.randint(1, 6))
          ^^^^^^^^^^^^^^
AttributeError: partially initialized module 'random' has no attribute 'randint' (most likely due to a circular import)

import random が自分自身を読み込んでいるため、循環 import と同じ形のメッセージになります。Python 3.14.7 では、原因と対処がそのまま表示されました。

AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/user/shadow/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)

別のファイルから読み込む形でも同じです。フォルダに json.py を置いた状態で import json すると、__file__ は自作のファイルを指していました。

/home/user/shadow/json.py          ← json.py がある状態
/usr/lib/python3.11/json/__init__.py   ← json.py を削除したあと

対処は、自作ファイルの名前を変えることです(例:random.py を dice.py に)。同じフォルダに __pycache__ があれば、合わせて削除しておくと確実です。「標準ライブラリの関数がないと言われる」ときは、print(モジュール.__file__) で読み込み元を確認すると、すぐに気付けます。

No module named 'requests'(pip 未導入・venv 違い)

外部ライブラリが見つからないケースです。新しく作った venv の Python と、OS の Python で比べました。

python3 -m venv ../v1
../v1/bin/python -c "import requests"
python3 -c "import requests; print(requests.__version__)"
ModuleNotFoundError: No module named 'requests'
2.34.2

同じマシンでも、Python ごとにインストール済みのライブラリは別々です。 OS の Python には入っていても、venv の Python には入っていません。「pip install したのに見つからない」ときは、インストールした先と、実行している Python がずれています。

確認の手順は次のとおりです。

python -c "import sys; print(sys.executable)"   # 実行している Python
python -m pip --version                          # その Python の pip
python -m pip list                               # その Python に入っているもの
python -m pip install requests                   # その Python に入れる

手元の venv では、sys.executable が /home/user/v1/bin/python、pip list は pip と setuptools だけでした。pip install ではなく python -m pip install と書くと、実行に使う Python と同じ場所へ確実に入ります。

状況 確認すること 対処
そもそも入れていない python -m pip list python -m pip install 名前
venv を有効化していない sys.executable が venv の中か source .venv/bin/activate
エディタが別の Python を使っている エディタのインタープリター設定 venv の Python を選ぶ
import 名とインストール名が違う No module named 'yaml' など 下の表の名前でインストール
大文字と小文字の違い No module named 'Calc' ファイル名どおりに書く

import する名前と、pip で指定する名前が異なるライブラリの例です。

import 文 インストールするときの名前
import yaml pyyaml
import cv2 opencv-python
import PIL pillow
import sklearn scikit-learn
import bs4 beautifulsoup4
import dotenv python-dotenv

venv の作り方・有効化のコマンドは、別記事「Python venvの使い方」で OS 別に紹介しています。

原因別チェックリスト

import のエラーが出たら、上から順に確認します。

順番 確認すること コマンド・見る場所
1 エラーの種類は何か No module named/relative import/cannot import name/partially initialized
2 どの Python で動かしているか python -c "import sys; print(sys.executable)"
3 どこを探しているか python -c "import sys; print(sys.path)"
4 どこで・どう実行したか ルートのフォルダで python -m パッケージ.モジュール にする
5 実際に読まれたファイルはどれか print(モジュール.__file__)
6 標準ライブラリと同じ名前のファイルがないか random.py・json.py・test.py などを改名
7 外部ライブラリは入っているか python -m pip show 名前
8 つづり・大文字小文字は合っているか ファイル名・定義と見比べる
9 お互いに import し合っていないか トレースバックに 2 つのファイルが交互に出ていないか

import できるかどうかだけを調べたいときは、importlib.util.find_spec が便利です。見つかれば場所、見つからなければ None が返りました。

import importlib.util
print(importlib.util.find_spec('app.models').origin)
print(importlib.util.find_spec('nothing_here'))
/home/user/shop/app/models/__init__.py
None

import 文の書き方の作法

PEP 8(Python の公式スタイルガイド)に沿った並べ方です。

# 1. 標準ライブラリ
import json
import sys
from pathlib import Path

# 2. 外部ライブラリ
import requests

# 3. 自作のモジュール
from app.models import Item, Order
from app.utils.money import add_tax
作法 内容
ファイルの先頭にまとめる 循環 import の回避など、理由があるときだけ関数の中に書く
3 つのグループに分け、空行で区切る 標準 → 外部 → 自作
1 行に 1 モジュール import os, sys ではなく 2 行に分ける(from x import a, b は 1 行でよい)
ワイルドカードは控えめに 名前の由来がわかる形で書く
並び順は道具に任せる Ruff の isort ルール(I)で自動整列できる

よくある質問

Q. __init__.py は必ず必要ですか? A. Python 3.3 以降は、なくても import できます(名前空間パッケージ)。ただし、初期化の処理や名前の引き上げが書けず、ツールによってはフォルダの扱いが変わります。通常のアプリでは、空のファイルでよいので置いておくのがおすすめです。

Q. 1 つ上のフォルダにあるモジュールを import するには? A. 「上のフォルダ」ではなく、「ルートのフォルダから見た名前」で考えると整理できます。ルートで python -m パッケージ.モジュール として実行すれば、どのファイルからも from app.utils.money import add_tax のように絶対 import で書けます。別のフォルダからも使いたい場合は pip install -e . で登録します。

Q. import a.b.c と from a.b import c は何が違いますか? A. 読み込まれるものは同じで、使うときの名前が違います。前者は a.b.c.func()、後者は c.func() と書きます。import a.b.c as c と書けば、後者と同じ名前で使えます。

Q. Jupyter Notebook で自作モジュールを書き換えても反映されません。 A. import は最初の 1 回だけ実行されるためです。importlib.reload(モジュール) で読み直すか、カーネルを再起動します。IPython の %load_ext autoreload と %autoreload 2 を使うと、実行のたびに自動で読み直されます(この拡張機能は手元では実行しておらず、IPython の公式ドキュメントで確認した内容です)。

まとめ

  • import の書き方は import・from ... import・as・相対 import(. ..)の組み合わせ。迷ったら import モジュール名
  • Python は sys.path を先頭から探す。ファイルを直接実行すると、先頭は「そのファイルのあるフォルダ」になる
  • ModuleNotFoundError は、ルートのフォルダで python -m パッケージ.モジュール と実行するのが最初の一手
  • 相対 import を含むファイルは直接実行せず、-m で実行する
  • partially initialized module は循環 import の目印。import を関数の中へ移すか、共通部分を別ファイルに出す
  • random.py など標準ライブラリと同じ名前は避ける。モジュール.__file__ で読み込み元を確認できる
  • 外部ライブラリは python -m pip install で、実行する Python と同じ場所に入れる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Python importの使い方|自作モジュールとエラー対処」

株式会社RJCでは、Python の import やパッケージ構成の考え方を、仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?