Pythonでプロジェクトの規模が大きくなると、ファイルを複数のディレクトリに分けて管理する場面が増えます。その際、パッケージ内部のモジュール同士を参照するために、相対パスを使ったimportが役立ちます。
相対パスによるimportとは、現在のモジュールが属するパッケージを基準にして、ドット表記(.や..)で参照先のモジュールを指定する方法です。完全修飾名(パッケージ名.モジュール名)を書かずに済むため、パッケージ内部のモジュール間で簡潔にimportできます。
この記事では、Pythonのimportで相対パスを指定する基本的な書き方から、よくあるエラーの対処法まで、ディレクトリ構成例とサンプルコード付きで解説していきます。
Pythonのimportで相対パスを指定する方法
Pythonの相対importを使うには、from文にドット(.)を付けてモジュールの位置を示します。ドット表記は、現在のモジュールが属するパッケージを基準とし、ドット1つ(.)が現在のパッケージ、ドット2つ(..)が親パッケージを表す記法です。基本的な書き方は、以下の通りです。
from . import module_name # 同じパッケージ内のモジュール
from .. import module_name # 親パッケージ内のモジュール
from .sub_package import module_name # サブパッケージ内のモジュール
なお、相対importは必ずfrom文と組み合わせて使います。import .utilsのように単独のimport文で書くとSyntaxErrorが発生するため注意してください。
また、相対importはパッケージの一部として読み込まれたモジュールからしか使用できません。通常は__init__.pyを配置したディレクトリをパッケージとして扱いますが、Python 3.3以降では__init__.pyがなくても名前空間パッケージとして認識されます(詳細はFAQを参照)。ここでは、以下の3つのパターンを解説します。
- 同じパッケージ内のモジュールをimportする
- 親パッケージのモジュールをimportする
- サブパッケージのモジュールをimportする
それぞれのパターンで使用するドット表記が異なるため、パッケージ構成に応じた使い分けが重要です。
それでは各パターンについて、解説していきます。
同じパッケージ内のモジュールをimportする
同じパッケージ内にあるモジュールをimportする場合は、ドット1つ(.)を使います。以下のディレクトリ構成を前提に、具体的な書き方を確認しましょう。
project/
├── __init__.py
├── main.py
└── utils.py # このモジュールをimportしたい
上記の構成で、main.pyからutils.pyをimportするコードは以下の通りです。
# main.py
from . import utils
# utils内の関数を使用
result = utils.calculate(10, 20)
print(result)
上記のコードでは、from . import utilsのドット(.)が「現在のモジュールが属するパッケージ」を意味しています。これにより、main.pyと同じパッケージにあるutils.pyモジュール全体をimportします。
モジュール内の特定の関数やクラスだけをimportしたい場合は、以下のように記述します。
# main.py
from .utils import calculate
# 直接関数を呼び出せる
result = calculate(10, 20)
print(result)
上記のコードでは、from .utils import calculateでutils.py内のcalculate関数だけをimportしています。この書き方を使うことによって、utils.calculate()ではなくcalculate()と直接呼び出せるようになります。
なお、このファイルをpython main.pyのように直接実行するとエラーが発生します。実行方法については後述の「no known parent packageエラーの対処法」で詳しく解説します。
親パッケージのモジュールをimportする
親パッケージにあるモジュールをimportする場合は、ドット2つ(..)を使います。以下のディレクトリ構成を前提に確認しましょう。
project/
├── __init__.py
├── config.py # このモジュールをimportしたい
└── sub_package/
├── __init__.py
└── handler.py # ここからconfig.pyをimportする
上記の構成で、handler.pyから親パッケージのconfig.pyをimportするコードは以下の通りです。
# sub_package/handler.py
from .. import config
# config内の変数を使用
db_host = config.DATABASE_HOST
print(db_host)
上記のコードでは、from .. import configのドット2つ(..)が「1つ上の階層のパッケージ」を意味しています。handler.pyの親パッケージであるproject直下のconfig.pyをimportします。
理論上は、ドット3つ(...)以上を使ってさらに上位のパッケージを参照することもできます。ただし、ドット3つ以上を使う相対importはコードの可読性が著しく下がるうえ、後述する「beyond top-level packageエラー」も発生しやすくなります。そのため、実務では深い階層を遡る場合は絶対importに切り替えるのが一般的です。
サブパッケージのモジュールをimportする
サブパッケージ(子パッケージ)内のモジュールをimportする場合は、ドット1つ(.)にサブパッケージ名を続けて記述します。以下のディレクトリ構成を前提に確認しましょう。
project/
├── __init__.py
├── main.py # ここからサブパッケージのモジュールをimportする
└── helpers/
├── __init__.py
└── formatter.py # このモジュールをimportしたい
上記の構成で、main.pyからhelpers/formatter.pyをimportするコードは以下の通りです。
# main.py
from .helpers import formatter
# formatter内の関数を使用
text = formatter.format_date("2026-04-20")
print(text)
上記のコードでは、from .helpers import formatterでサブパッケージhelpersの中にあるformatter.pyをimportしています。ドット(.)は現在のパッケージを示し、そこからhelpersパッケージ内のformatterモジュールを参照します。
サブパッケージ内の特定の関数を直接importすることも可能です。
# main.py
from .helpers.formatter import format_date
# 直接関数を呼び出せる
text = format_date("2026-04-20")
print(text)
上記のコードでは、from .helpers.formatter import format_dateでformatter.py内のformat_date関数だけをimportしています。ドット区切りでパッケージ階層をたどることによって、ネストされたパッケージ内のモジュールにもアクセスできます。
Pythonの相対パスimportで発生するエラーの対処法
Pythonの相対importでは、実行方法やパッケージ構成によってエラーが発生することがあります。ここでは、相対import特有で特によく遭遇する2つのエラーについて、原因と対処法を解説します。
- no known parent packageエラーの対処法
- beyond top-level packageエラーの対処法
どちらのエラーもPythonのパッケージ構造に関する仕様が原因であり、正しい実行方法を理解することによって解決できます。
それでは各エラーについて、解説していきます。
no known parent packageエラーの対処法
ImportError: attempted relative import with no known parent packageは、相対importを使ったファイルを直接実行した場合に発生するエラーです。
# main.py を直接実行するとエラーになる
# python main.py ← この実行方法ではエラー
from . import utils # ImportError が発生
このエラーの原因は、Pythonが直接実行されたファイルに対して親パッケージの情報を与えないことにあります。ファイルをpython main.pyで直接実行すると、そのファイルのモジュール名(__name__)は__main__となり、親パッケージを示す__package__属性はNoneになります。相対importは__package__を基準に解決されるため、これが設定されていないとimportできずエラーになります。
このエラーを解決するには、-mオプションを使ってモジュールとして実行します。以下のように、プロジェクトの親ディレクトリからpython -m パッケージ名.モジュール名の形式で実行してください。
# ディレクトリ構成:
# workspace/
# └── project/
# ├── __init__.py
# ├── main.py
# └── utils.py
# workspace/ で以下を実行
python -m project.main
-mオプションで実行すると、対象ファイルはパッケージの一部としてロードされ、__package__属性に親パッケージ名(この例ではproject)が設定されます。これによってパッケージ文脈が与えられ、main.py内の相対importが正しく解決されるようになります。
なお、どうしても直接実行したい場合や、より可搬性の高いコードにしたい場合は、相対importを絶対importに書き換えるのも有効な解決策です。
beyond top-level packageエラーの対処法
attempted relative import beyond top-level packageというメッセージのエラー(Pythonのバージョンや実行状況によってValueErrorまたはImportErrorとして発生)は、ドット表記でトップレベルパッケージより上の階層を参照しようとした場合に発生します。
ここで言うトップレベルパッケージとは、Pythonが実行時にsys.pathから認識できる最上位のパッケージのことです。必ずしもプロジェクトの最上位ディレクトリと一致するわけではなく、実行方法によって変わる点に注意してください。
# ディレクトリ構成:
# project/
# ├── __init__.py
# └── sub_package/
# ├── __init__.py
# └── module_a.py
# module_a.py で以下を書くとエラー
from ... import something # トップレベルパッケージの外を参照しようとしている
上記のコードでは、module_a.pyからドット3つ(...)で2つ上のパッケージを指定しています。module_a.pyはsub_package内にあるため、1つ上はproject、2つ上はprojectの外側になります。projectがトップレベルパッケージの場合、その外側は参照できないためエラーが発生します。
このエラーを解決するには、パッケージ構成を見直すか、絶対importに切り替える必要があります。以下の2つの方法が有効です。
# 方法1: ドットの数を減らして、パッケージ階層内に収める
from .. import config # 1つ上のパッケージ(project直下)まで
# 方法2: 絶対importに切り替える
from project import config # トップレベルからの完全修飾名で指定
上記のように、ドットの数がパッケージの階層を超えないように調整するか、絶対importに切り替えることによってエラーを回避できます。実務では、深い階層からの相対importが必要になった場合は、絶対importを使う方がコードの可読性も高くなります。
Pythonの相対パスimportに関するよくある質問
__init__.pyは必要ですか?
Python 3.3以降では、__init__.pyがなくてもディレクトリをパッケージとして認識する「名前空間パッケージ」という仕組みが導入されているため、必須ではありません。ただし、通常のアプリケーション開発では__init__.pyを配置することが推奨されます。
-
__init__.pyあり: 通常パッケージとして認識される -
__init__.pyなし: 名前空間パッケージとして扱われる
__init__.pyを配置することによって、パッケージの初期化処理を定義したり、パッケージの公開APIを明示したりできます。また、パッケージの境界が明示的になり、単一ソースツリーのアプリケーションでは挙動を把握しやすくなります。特別な理由がない限り配置しておきましょう。
相対パスと絶対パスのどちらを使うべきですか?
Pythonの公式スタイルガイド(PEP 8)では、絶対importの使用が推奨されています。ただし、明示的な相対importも許容される代替手段として位置付けられています。
Absolute imports are recommended, as they are usually more readable and tend to be better behaved (...).
However, explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose.(絶対importは通常読みやすく挙動も安定しているため推奨されます。ただし、複雑なパッケージ構成で絶対importを使うと冗長になる場合には、明示的な相対importも許容される代替手段となります。)
出典: PEP 8 – Style Guide for Python Code
絶対importと相対importの特徴を整理すると、以下のようになります。
| import方法 | メリット | デメリット |
|---|---|---|
| 絶対import | モジュールの位置が明確で可読性が高い | パッケージ名を変更すると修正箇所が増えやすい |
| 相対import | 記述が簡潔でパッケージ名の変更に強い | パッケージ構成の変更時に修正が必要になりやすい |
基本方針としては絶対importを第一候補としつつ、パッケージ内部で記述が冗長になる場合は相対importも併用する、という使い分けが現実的です。
sys.path.appendを使った方法は推奨されますか?
sys.path.append()を使ってモジュール検索パスに手動でディレクトリを追加する方法は、アプリケーション本体やライブラリコードでは推奨されません。
- 環境依存のパスをコードに埋め込むことになる
- 他の開発者が同じ環境を再現できない可能性がある
- パッケージ構成を正しく設計すれば不要になる
ただし、一時的な検証スクリプトやJupyter Notebookでの開発など、可搬性が重視されない場面で使われることはあります。通常の相対importでエラーが発生した場合は、sys.path.appendに頼るのではなく、python -mオプションでモジュールとして実行する、あるいは絶対importに書き換える方法を優先してください。
※上記コンテンツの内容やソースコードはAIで確認・デバッグしておりますが、間違いやエラー、脆弱性などがある場合は、コメントよりご報告いただけますと幸いです。
ITやプログラミングに関するコラム
PythonをWebで実行する方法
共通テスト「情報Ⅰ」2年目で変わる、日本の教育と学び方
gitでブランチ(branch)を切り替える方法
git cloneでブランチを指定する方法
64GBのメモリが必要な人・不要な人の特徴
PCを再起動するコマンド一覧
CapsLock以外で大文字になる原因【Windows編】
パソコンで大文字になるのを解除する方法
面白いAIの活用事例を業界別に紹介
Gitでcommit(コミット)を取り消す方法
ITやプログラミングに関するニュース
サイボウズがkintone AIを正式提供、β版から約1年を経てクレジット制を導入
ロゼッタのラクヤクAIがCSRドラフト作成期間を90%以上短縮、従来4週間を約2日に
AI CROSSが不動産業界向け生成AI伴走支援を開始、アスコットの業務AI実装を実践サポート
日本情報クリエイトが「オーナー提案AIロボⅡ」売買査定を刷新、月1万円からW査定が回数無制限に
Wur株式会社がAI新規事業診断サービス「MVP事業診断レポート」をリリース、12の質問で事業構想を約10分で分析
バトンズがM&A専門家向け「AI概要書」β版を提供開始、企業概要書のドラフトを最速3分で自動生成
SCSKが観光DXサービス「Connexia」を開発、首里城公園でNFT活用の周遊促進が始動
Verdent AI発表、エンジニア不要でソフトウェアを構築する「AIエンジニアリングチーム」が登場
ゼネラルBREXAテクノロジーが外食・小売向けAIサービス「aimana」を開発、店長の意思決定をデータで支援
田中組がKencopa工程AIエージェント製品版を先行利用開始、建設現場の工程管理属人化を解消へ
