- 技術 💻
モジュールとは|プログラムでの意味・パッケージとの違いを3言語で実例解説
結論
モジュールとは、関連する関数やクラスを1つにまとめ、他から読み込んで使える部品です。Python・JavaScript では多くの場合「1ファイル」が1モジュールです。
この記事でわかること
- 関数・クラス・パッケージなど5語の違い表
- Python・JS・Java の3言語で動かした実行結果
- エラー原文19種と循環 import の直し方
小さな部品(関数・クラス)をファイルにまとめたものがモジュールで、モジュールを束ねるとパッケージ、配布できる形にするとライブラリになります。
flowchart TD
A[関数・クラス 処理の単位] --> B[モジュール 1ファイル]
B --> C[パッケージ フォルダ]
C --> D[ライブラリ 配布する単位]
D --> E[アプリが import して使う]
| 用語 | 何のまとまりか | Python の例 |
|---|---|---|
| モジュール | 関数・クラス・変数を入れた1ファイル | price.py |
| 関数 | 1つの処理 | def with_tax(amount): |
| クラス | データと処理の組 | class Cart: |
| パッケージ | モジュールを入れたフォルダ | shop/(__init__.py 入り) |
| ライブラリ | 目的ごとに配布されるコードの集まり | 標準の json、pip の requests |
| Java のモジュール | パッケージを束ねた単位(Java 9〜) | module-info.java |
確認環境:Ubuntu 24.04.4 LTS で、Python 3.13.7(循環 import のメッセージは 3.11.15・3.12.3・3.14.7 でも比較)、Node.js 22.22.2、OpenJDK 25.0.4.1 を使い、同じ「カート合計を計算するアプリ」を3言語でモジュールに分けて実行し、エラーをわざと起こして原文を集めました。仕様は Python ドキュメント(Tutorial・FAQ・What's New 3.13)、Node.js ドキュメント(Modules: ECMAScript modules)、MDN、OpenJDK の JEP 261 で確かめています(確認日 2026年10月17日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「モジュールとは|プログラムでの意味・パッケージとの違いを3言語で実例解説」
モジュールとは:プログラムでの意味
モジュール(module)は、まとまった機能を持ち、他の部分から独立して取り替えられる部品のことです。 プログラミングでは、関連する関数・クラス・変数を1つのファイル(または言語が決めた単位)にまとめ、別のファイルから import して使えるようにしたものを指します。
Python のドキュメントは、モジュールを次のように定義しています。
A module is a file containing Python definitions and statements.(モジュールは、Python の定義と文を入れたファイルです)
つまり price.py というファイルを作れば、それだけで price という名前のモジュールになります。モジュールの中で定義した名前は、そのモジュールの中に閉じています。別のモジュールに同じ名前の関数があっても、price.with_tax と cart.with_tax のように区別できます。この「名前の置き場所」を 名前空間(namespace) と呼びます。
IT 以外の「モジュール」
もとはラテン語の modulus(小さな計測の単位)から来た言葉で、分野によって意味が少し変わります。共通するのは「規格がそろった、組み合わせて使う部品やまとまり」という点です。
| 分野 | モジュールの意味 | 例 |
|---|---|---|
| ソフトウェア | 機能ごとにまとめた、交換できるプログラムの単位 | price.py・cart.js |
| 電子機器 | 複数の部品を組み合わせ、1つの機能を持たせたかたまり | 通信モジュール・カメラモジュール |
| 建築 | 各部分の大きさをそろえる基準の寸法 | 畳の寸法を基準にした間取り |
| 宇宙船 | 独立して動ける構成単位 | 宇宙ステーションの実験モジュール |
| 教育 | 学習時間の単位 | 15分を1モジュールとする授業 |
この記事では、ソフトウェア(プログラム)のモジュールを扱います。
関数・クラス・モジュール・パッケージ・ライブラリの違い
5つの言葉は「大きさ」と「何を入れる箱か」で区別します。
| 言葉 | 中に入るもの | 単位 | 読み込み方(Python) |
|---|---|---|---|
| 関数 | 処理の手順 | コードのかたまり | 呼び出す with_tax(300) |
| クラス | データ(属性)と処理(メソッド) | コードのかたまり | 作る Cart() |
| モジュール | 関数・クラス・変数 | 1ファイル | import shop.price |
| パッケージ | モジュール(と下のパッケージ) | フォルダ | from shop import cart |
| ライブラリ | モジュール・パッケージの集まり | 配布物(pip・npm 等) | pip install してから import |
まぎらわしいのは、次の3点です。
ライブラリ requests は、たくさんのモジュールが入ったパッケージとして配布されている
Python:モジュール(ファイル)⊂ パッケージ(フォルダ)/Java:パッケージ ⊂ モジュール(module-info.java)
express パッケージを入れると、node_modules/express の下に多数の .js モジュールが置かれる
3言語で「モジュール」が指すもの
| Python | JavaScript(ES Modules) | Java | |
|---|---|---|---|
| モジュールの単位 | 1つの .py ファイル | 1つの .js / .mjs ファイル | module-info.java を持つパッケージの集まり |
| 外に見せる書き方 | 何もしない(全部見える。_ 始まりは「内部用」の目印) |
export を付けたものだけ |
exports したパッケージの public 型だけ |
| 使う書き方 | import・from … import |
import … from |
requires(module-info.java)+ import |
| 何回実行されるか | 最初の import で1回 | 最初の import で1回 | クラスは必要になったとき1回読み込み |
| 仕様に入った版 | 最初から | ES2015 | Java 9(JEP 261) |
モジュールに分ける理由:凝集度と結合度
1つのファイルに全部を書いても、プログラムは動きます。それでも分けるのは、次の3つのためです。
1直す場所が1か所に決まる
税率が変わったら price モジュールだけを直します。カートや画面のコードを探し回らずに済み、直した影響も price を使う所に限られます。
2分担とテストがしやすい
モジュールごとに担当を分けられます。price だけを import してテストでき、他の部分が完成していなくても確かめられます。
3使い回せる
金額計算を別のアプリでも使いたいとき、price モジュールをそのまま import できます。広く使うものはライブラリとして配布します。
よい分け方の物差し:凝集度は高く、結合度は低く
モジュールの分け方の良し悪しは、2つの物差しで測ります。
- 凝集度(モジュール強度):1つのモジュールの中身が、どれだけ1つの目的に集中しているか。高いほどよい。
- 結合度:モジュール同士が、どれだけ互いの中身に頼っているか。低いほどよい。
結合度は、次の6段階で説明されることが多く、上ほど望ましい形です(G. J. Myers の分類として広く使われています)。
| 結合の種類 | どう結びついているか | 例 |
|---|---|---|
| データ結合 | 必要な値だけを引数で渡す | with_tax(420) |
| スタンプ結合 | 構造体・オブジェクトごと渡し、一部だけ使う | カート全体を渡して合計だけ使う |
| 制御結合 | 相手の動き方を切り替えるフラグを渡す | calc(x, mode="tax") |
| 外部結合 | 外で決まった形式・装置を共有する | 同じ外部ファイル形式を読み書き |
| 共通結合 | 共有のグローバル変数を読み書きする | 全モジュールが config.TAX を書き換える |
| 内容結合 | 相手の内部(非公開の部分)を直接触る | price._round_yen() を外から呼ぶ |
凝集度は、高い順に「機能的・情報的・連絡的・手順的・時間的・論理的・暗合的」の7段階で説明されます。「金額の計算だけ」を入れた price は機能的(1つの機能に集中)、「起動時にやること全部」を入れたモジュールは時間的(同じタイミングという理由だけで集まった)にあたります。
モジュール設計の進め方
役割を書き出す
アプリがすることを「商品を持つ」「金額を計算する」「画面に出す」のように動詞で並べる
外に見せるものを決める
他のモジュールが使う関数・クラスだけを公開する。それ以外は内部用にする
依存の向きを1方向にそろえる
画面 → カート → 金額計算 のように、上から下へだけ import する
モジュール構成図にする
箱(モジュール)と矢印(import する向き)で描き、矢印が輪になっていないかを見る
Python のモジュール:ファイルを作って import する
ここからは、同じ「カートの合計(税込)を出すアプリ」を3言語でモジュールに分けます。Python の構成は次のとおりです。
proj/
|-- app.py ← 実行するファイル
`-- shop/ ← パッケージ(フォルダ)
|-- __init__.py ← これがあると shop はパッケージになる
|-- price.py ← モジュール:金額の計算
`-- cart.py ← モジュール:カート
# shop/price.py ― 金額の計算だけを受け持つ
TAX_RATE = 0.10
print(f"[price] 読み込み中 __name__={__name__}")
def with_tax(amount: int) -> int:
return int(amount * (1 + TAX_RATE))
def _round_yen(x: float) -> int: # 先頭の _ は「外から使わない」目印
return int(x)
if __name__ == "__main__": # 直接実行したときだけ動く(import では動かない)
print("試し計算:", with_tax(1000))
# shop/cart.py ― カートの合計を出す。金額の計算は price に任せる
from shop.price import with_tax
class Cart:
def __init__(self):
self.items = []
def add(self, name: str, price: int):
self.items.append((name, price))
def total(self) -> int:
return with_tax(sum(p for _, p in self.items))
# app.py
import shop.price
import shop.price # 2回目は読み込み直さない
from shop.cart import Cart
import sys
cart = Cart()
cart.add("ノート", 300)
cart.add("ペン", 120)
print("合計(税込):", cart.total())
print("shop.price の正体:", shop.price)
print("型:", type(shop.price))
print("公開している名前:", [n for n in dir(shop.price) if not n.startswith("__")])
print("sys.modules に入っている shop 関係:", sorted(k for k in sys.modules if k.startswith("shop")))
print("app.py の __name__:", __name__)
$ python3 app.py
[price] 読み込み中 __name__=shop.price
合計(税込): 462
shop.price の正体: <module 'shop.price' from '/home/dev/proj/shop/price.py'>
型: <class 'module'>
公開している名前: ['TAX_RATE', '_round_yen', 'with_tax']
sys.modules に入っている shop 関係: ['shop', 'shop.cart', 'shop.price']
app.py の __name__: __main__
出力から分かることは4つです。
- import は2回書いても、中身の実行は1回。
[price] 読み込み中は1行しか出ていません。読み込んだモジュールはsys.modulesに保存され、2回目以降はそれを使います。Python のドキュメントにも、モジュールの文は「最初に import されたときだけ」実行されるとあります。 - モジュールは
module型のオブジェクト。どのファイルから読んだかを持っています。 _round_yenも外から見える。Python には「非公開」を強制する仕組みは無く、先頭の_は「内部用」という約束です。__name__は、import されたときはモジュール名、直接実行したときは__main__になります。
$ python3 -m shop.price
[price] 読み込み中 __name__=__main__
試し計算: 1100
$ python3 -c "import shop.price"
[price] 読み込み中 __name__=shop.price
if __name__ == "__main__": の下は、-m で直接実行したときだけ動き、import では動きません。モジュールに「試し計算」や簡単なテストを置くときに使います。
Python はどこからモジュールを探すか
import shop を実行すると、Python は sys.path に並んだ場所を前から順に探します。ドキュメントによると、sys.path の先頭は次の順です。
- 実行したスクリプトがあるフォルダ(
-cや対話モードでは今いるフォルダ) - 環境変数
PYTHONPATHに書いたフォルダ - インストール先ごとの既定の場所(標準ライブラリ・
site-packages)
実行したスクリプトのフォルダが標準ライブラリより前にあるため、自分のファイルに random.py のような標準ライブラリと同じ名前を付けると、標準ライブラリの代わりに自分のファイルが読まれます(エラーの節で実物を出します)。
import の書き方(from … import・as・相対 import)の一覧は、関連記事「Python importの使い方|自作モジュールとエラー対処」で扱っています。
JavaScript のモジュール(ES Modules):export したものだけが見える
JavaScript のモジュールの標準は ES Modules(ESM) です。1ファイルが1モジュールで、export を付けたものだけが他のファイルから使えます。
js/
|-- package.json ← "type": "module" で .js を ES Modules として扱う
|-- app.js
`-- shop/
|-- price.js
`-- cart.js
{
"name": "shop-app",
"type": "module"
}
// shop/price.js ― 金額の計算だけを受け持つ
export const TAX_RATE = 0.10;
console.log(`[price] 読み込み中 ${import.meta.url}`);
export function withTax(amount) {
return Math.trunc(amount * (1 + TAX_RATE));
}
function roundYen(x) { return Math.trunc(x); } // export しないものは外から見えない
// shop/cart.js
import { withTax } from './price.js';
export default class Cart {
#items = [];
add(name, price) { this.#items.push({ name, price }); }
total() { return withTax(this.#items.reduce((s, i) => s + i.price, 0)); }
}
// app.js
import Cart from './shop/cart.js';
import * as price from './shop/price.js'; // 2回目は読み込み直さない
const cart = new Cart();
cart.add('ノート', 300);
cart.add('ペン', 120);
console.log('合計(税込):', cart.total());
console.log('price の中身:', price);
console.log('roundYen は見える?', 'roundYen' in price);
try { price.TAX_RATE = 0.08; } catch (e) { console.log(`${e.name}: ${e.message}`); }
$ node app.js
[price] 読み込み中 file:///home/dev/js/shop/price.js
合計(税込): 462
price の中身: [Module: null prototype] {
TAX_RATE: 0.1,
withTax: [Function: withTax]
}
roundYen は見える? false
TypeError: Cannot assign to read only property 'TAX_RATE' of object '[object Module]'
Python との違いがはっきり出ています。
- export しなかった
roundYenは、外から見えない(false)。公開・非公開が言語の仕組みで決まります。 - import したものは読み取り専用。外から
TAX_RATEを書き換えようとするとTypeErrorになります。共通結合(共有変数の書き換え)が起きにくい作りです。 - 2か所から import しても、price の実行は1回です。MDN にも、モジュールは複数の場所から参照されても一度しか実行されないとあります。
export の2種類と、CommonJS との違い
| 書き方 | export 側 | import 側 |
|---|---|---|
| 名前付き export | export function withTax() {} |
import { withTax } from './price.js' |
| default export(1ファイル1つ) | export default class Cart {} |
import Cart from './cart.js' |
| まとめて読む | (名前付き export 全部) | import * as price from './price.js' |
| CommonJS(Node.js の従来方式) | module.exports = { withTax } |
const { withTax } = require('./price') |
Node.js のドキュメントでは、ファイルを ES Modules として扱う目印は「拡張子 .mjs」「package.json の "type": "module"」「--input-type=module」の3つです。目印が無いときは、中に ES Modules の構文があるかを見て判断します。ブラウザでは <script type="module" src="main.js"> と書くとモジュールとして読み込まれます。
ESM と CommonJS の書き方の違いは、関連記事「JavaScript import/export入門|ESMとCommonJSの違い」で扱っています。
Java のモジュールシステム:module-info.java
Java の「モジュール」は、Python・JavaScript とは大きさが違います。Java のモジュールは、複数のパッケージを束ね、どのパッケージを外に見せるかを module-info.java に書いた単位です。Java 9 で入った仕組み(Java Platform Module System、JEP 261)で、JEP 261 は目的を「信頼できる構成(reliable configuration)と強いカプセル化(strong encapsulation)」としています。
src/
|-- com.example.shop/ ← モジュール shop
| |-- module-info.java
| `-- com/example/shop/
| |-- api/Cart.java ← 外に見せるパッケージ
| `-- internal/Price.java ← 内部用のパッケージ
`-- com.example.app/ ← モジュール app
|-- module-info.java
`-- com/example/app/Main.java
// src/com.example.shop/module-info.java
module com.example.shop {
exports com.example.shop.api; // 外に見せるのは api パッケージだけ
}
// src/com.example.app/module-info.java
module com.example.app {
requires com.example.shop; // shop モジュールを使う
}
// src/com.example.shop/com/example/shop/internal/Price.java
package com.example.shop.internal;
public class Price {
public static final double TAX_RATE = 0.10;
public static int withTax(int amount) { return (int) (amount * (1 + TAX_RATE)); }
}
// src/com.example.shop/com/example/shop/api/Cart.java
package com.example.shop.api;
import com.example.shop.internal.Price;
import java.util.ArrayList;
import java.util.List;
public class Cart {
private final List<Integer> prices = new ArrayList<>();
public void add(String name, int price) { prices.add(price); }
public int total() {
return Price.withTax(prices.stream().mapToInt(Integer::intValue).sum());
}
}
// src/com.example.app/com/example/app/Main.java
package com.example.app;
import com.example.shop.api.Cart;
public class Main {
public static void main(String[] args) {
Cart cart = new Cart();
cart.add("ノート", 300);
cart.add("ペン", 120);
System.out.println("合計(税込): " + cart.total());
System.out.println("Main のモジュール: " + Main.class.getModule());
System.out.println("Cart のモジュール: " + Cart.class.getModule());
}
}
--module-source-path でモジュールごとのフォルダをまとめてコンパイルし、-p(--module-path)でモジュールの置き場所を指定して実行します。
$ javac -d out --module-source-path src -m com.example.app
$ find out -type f | sort
out/com.example.app/com/example/app/Main.class
out/com.example.app/module-info.class
out/com.example.shop/com/example/shop/api/Cart.class
out/com.example.shop/com/example/shop/internal/Price.class
out/com.example.shop/module-info.class
$ java -p out -m com.example.app/com.example.app.Main
合計(税込): 462
Main のモジュール: module com.example.app
Cart のモジュール: module com.example.shop
$ java -p out --describe-module com.example.shop
com.example.shop file:///home/dev/jmod/out/com.example.shop/
exports com.example.shop.api
requires java.base mandated
contains com.example.shop.internal
--describe-module の出力で、exports(外に見せる)と contains(中にあるが見せない)が分かれているのが分かります。requires java.base mandated は、どのモジュールも自動で java.base(String や List が入った基本のモジュール)を使うという意味です。JDK 自体もモジュールに分かれていて、java --list-modules は JDK 25.0.4.1 で69個を表示しました。
クラスパスとモジュールパスで何が変わるか
同じ Price クラスを、module-info.java なしでクラスパス(-cp)に置くと、public なので内部用のパッケージも外から使えてしまいます。
$ java -cp cp com.example.app.UseInternal
internal を直接使う: 1100
モジュール: unnamed module @7ad041f3
クラスパスに置いたクラスは「名前の無いモジュール(unnamed module)」に入ります。モジュールパスに置くと、exports していないパッケージは public でも外から使えません(次の節のエラー)。パッケージとクラスパスの基本は、関連記事「Javaのpackageとimportの使い方|クラスパスの通し方」で扱っています。
モジュール構成図の書き方
モジュール構成図は、モジュールを箱、「import する(依存する)向き」を矢印で描いた図です。 設計のときは「矢印が一方向に流れているか」「輪(循環)が無いか」を見るために使います。この記事のアプリを描くと次の形です。
flowchart TD
A[app 画面・実行] --> B[cart カート]
A --> C[price 金額計算]
B --> C
C --> D[標準ライブラリ]
描くときの決まりは3つです。
| 決まり | 理由 |
|---|---|
| 矢印は「使う側 → 使われる側」 | import の向きと一致させると、コードと照らし合わせやすい |
| 上に使う側、下に使われる側を置く | 矢印が全部下向きなら、循環が無いと一目で分かる |
| 公開している関数・クラス名を箱に添える | 結合が「データ結合」で済んでいるかを確かめやすい |
Java では、jdeps でコンパイル済みのモジュールから実際の依存を出せます。図と実物がずれていないかの確認に使えます。
$ jdeps --module-path out -s -m com.example.app
com.example.app -> com.example.shop
com.example.app -> java.base
よくあるエラーと直し方(Python・JavaScript・Java)
モジュールまわりのエラーは、「見つからない」「名前が無い」「見えない」「循環している」の4種類にほぼ分かれます。以下はすべて、上のアプリで実際に起こしたエラーの原文です。
Python
| エラー(原文) | 原因 | 直し方 |
|---|---|---|
ModuleNotFoundError: No module named 'shop.prices' |
モジュール名の打ち間違い | ファイル名と import の綴りをそろえる |
ModuleNotFoundError: No module named 'requests' |
ライブラリが入っていない(別の Python に入っている) | python3 -m pip install requests(実行する Python と同じものに入れる) |
ModuleNotFoundError: No module named 'shop' |
別のフォルダから -c などで実行し、sys.path に proj が無い |
proj に移動して実行する、または python3 -m で実行する |
ImportError: attempted relative import with no known parent package |
from .price import … を書いたファイルを直接実行した |
python3 -m shop.report のようにパッケージ名で実行する |
AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/dev/shadow/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module) |
自分のファイル名が標準ライブラリと同じ | ファイル名を変え、__pycache__ も消す |
TypeError: 'module' object is not callable. Did you mean: 'datetime.datetime(...)'? |
モジュールを関数のように呼んだ | from datetime import datetime か datetime.datetime(...) |
ImportError: cannot import name 'find_user' from partially initialized module 'pkg.users' (most likely due to a circular import) |
循環 import | 次の節 |
random.py の例では、Python 3.13.7 は次のように原因まで示します。3.13 で追加されたメッセージです。
$ python3 random.py
Traceback (most recent call last):
...
print(random.randint(1, 6))
^^^^^^^^^^^^^^
AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/dev/shadow/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)
No module named の原因の全パターン(venv・pip の入れ先違い・IDE の Python)は、関連記事「ModuleNotFoundErrorの原因と直し方|No module named」で扱っています。
JavaScript(Node.js 22.22.2)
| エラー(原文) | 原因 | 直し方 |
|---|---|---|
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/dev/js/shop/price' imported from /home/dev/js/e1.js + Did you mean to import "./shop/price.js"? |
ESM の相対 import で拡張子を省いた | './shop/price.js' と拡張子まで書く |
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'express' imported from /home/dev/js/e2.js |
パッケージが入っていない | npm install express |
SyntaxError: The requested module './shop/price.js' does not provide an export named 'withtax' |
export に無い名前(大文字小文字違い)を import した | export 側の名前と完全に合わせる |
Error: Cannot find module './shop/prices'(code: 'MODULE_NOT_FOUND') |
CommonJS の require でファイルが無い | パスと綴りを直す |
SyntaxError: Cannot use import statement outside a module |
.cjs(CommonJS)のファイルで import を書いた |
拡張子を .mjs にするか、require で書く |
ReferenceError: require is not defined in ES module scope, you can use import instead |
ES Modules の中で require を使った | import に書き換える |
ReferenceError: Cannot access 'DEFAULT_USER' before initialization |
循環 import で、初期化前の値を読んだ | 次の節 |
Node.js のドキュメントでは、import で相対パスを指定するときは拡張子が必須で、フォルダの index.js も省略できないと書かれています。CommonJS の require は拡張子を補ってくれるので、書き換えたときにこのエラーが出やすくなります。
package.json に "type" を書かずに import を使うと、Node.js 22.22.2 では動きはしますが、次の警告が出ました。
(node:1466) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///home/dev/js2/a.js is not specified and it doesn't parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to /home/dev/js2/package.json.
Cannot find module の原因の一覧は、関連記事「Node.js Cannot find moduleの原因と直し方」で扱っています。
Java(JDK 25)
| エラー(原文) | 原因 | 直し方 |
|---|---|---|
error: package com.example.shop.internal is not visible + (package com.example.shop.internal is declared in module com.example.shop, which does not export it) |
exports していないパッケージを使った | 公開する API を使う。必要なら提供側に exports を足す |
error: package com.example.shop.api is not visible + (package com.example.shop.api is declared in module com.example.shop, but module com.example.app does not read it) |
module-info.java に requires が無い |
requires com.example.shop; を足す |
error: module not found: com.example.shopp |
requires のモジュール名の打ち間違い・-p に無い | 名前と -p の場所を確かめる |
error: cyclic dependence involving com.example.app |
モジュール同士が requires し合っている | 共通部分を別モジュールに切り出す |
java.lang.module.FindException: Module com.example.shop not found, required by com.example.app |
実行時の -p に必要なモジュールが無い |
-p に shop の場所(またはその jar)を足す |
Java のモジュールでは、循環はコンパイルの時点で止まります。Python・JavaScript は実行してみるまで分かりません。
循環 import の直し方
循環 import は、モジュール A が B を import し、B も A を import している状態です。 片方がまだ読み込みの途中なのに、もう片方がその中の名前を使おうとして失敗します。
Python で、users と orders が互いに from … import すると次のようになります。
# pkg/users.py
from pkg.orders import create_order
def find_user(uid):
return {"id": uid}
# pkg/orders.py
from pkg.users import find_user
def create_order(uid):
return {"user": find_user(uid)}
$ python3 main3.py # 中身は import pkg.users の1行
ImportError: cannot import name 'find_user' from partially initialized module 'pkg.users' (most likely due to a circular import) (/home/dev/circ/pkg/users.py)
partially initialized module(初期化の途中のモジュール)と出たら、循環 import です。
同じ循環でも、Python 3.13 以降では文言が変わる場合があります。 パッケージに入れず、実行するスクリプトと同じフォルダに users.py・orders.py を置いた場合、版によって次のように変わりました。
| Python の版 | エラーの最後の行 |
|---|---|
| 3.11.15・3.12.3 | cannot import name 'find_user' from partially initialized module 'users' (most likely due to a circular import) |
| 3.13.7・3.14.7 | cannot import name 'find_user' from 'users' (consider renaming '/home/dev/circ/users.py' if it has the same name as a library you intended to import) |
3.13 以降のメッセージは名前の重なりを疑うように読めますが、原因は循環です。consider renaming と出ても、ファイル名がライブラリと重なっていなければ、import が輪になっていないかを先に見ます。
直し方は、Python の FAQ にある方法を含めて3つです。
名前を取り出すのが「関数を呼んだ時点」まで遅れるので、そのころには users の読み込みが終わっている
Python の FAQ が挙げる方法。使う場所が1つの関数だけのときに向く
いちばん根本的。モジュール構成図で矢印が輪になっていないかを確かめる
直し方1で orders を書き換えると、同じ循環のままでも動きました。
# pkg/orders.py(直した後)
from pkg import users
def create_order(uid):
return {"user": users.find_user(uid)}
$ python3 main3.py # import pkg.users → from pkg.orders import create_order → print(create_order(7))
{'user': {'id': 7}}
JavaScript でも、読み込み途中のモジュールの const をトップレベルで読むと失敗します。
// users.js
import { createOrder } from './orders.js';
export const DEFAULT_USER = { id: 0 };
export function findUser(id) { return { id }; }
// orders.js(直す前)
import { DEFAULT_USER } from './users.js';
console.log('orders: DEFAULT_USER =', DEFAULT_USER); // users.js の初期化前に読んでいる
export function createOrder(id) { return { id }; }
$ node main.js # 中身は import './users.js' の1行
file:///home/dev/js/circ/orders.js:2
ReferenceError: Cannot access 'DEFAULT_USER' before initialization
トップレベルで読むのをやめ、関数の中で使うようにすると、呼ばれる時点では初期化が終わっているので動きます。
// orders.js(直した後)
import { DEFAULT_USER } from './users.js';
export function createOrder(id) { return { user: DEFAULT_USER, id }; }
$ node main.js # import { createOrder } from './orders.js' → console.log(createOrder(7))
{ user: { id: 0 }, id: 7 }
Python の循環インポートを直すパターンの詳細は、関連記事「cannot import nameの直し方|Pythonの循環インポート」で扱っています。
よくある質問
Q. モジュールとは何ですか。
A. まとまった機能を持ち、他から独立して取り替えられる部品のことです。プログラムでは、関連する関数・クラスをまとめ、import して使えるようにしたファイル(Java では module-info.java で定義した単位)を指します。
Q. モジュールとはプログラムでいうと何ですか。関数やクラスとの違いは。 A. 関数は1つの処理、クラスはデータと処理の組で、モジュールはそれらを入れる「1ファイル」です。モジュールを入れたフォルダがパッケージ、配布できる集まりがライブラリです。「関数・クラス・モジュール・パッケージ・ライブラリの違い」の表にまとめています。
Q. モジュール構成図とは何ですか。 A. モジュールを箱、import する向きを矢印で描いた図です。矢印が上から下へ一方向に流れ、輪になっていないかを確かめるのに使います。Java では jdeps で実際の依存を出せます。
Q. モジュール設計で大事なことは何ですか。 A. 1つのモジュールを1つの目的に集中させ(凝集度を高く)、モジュール同士は必要な値だけを受け渡す(結合度を低く)ことです。公開するものを絞り、依存の向きを一方向にそろえます。
Q. Python のモジュールとパッケージの違いは何ですか。
A. モジュールは1つの .py ファイル、パッケージはモジュールを入れたフォルダ(通常は __init__.py を置く)です。shop.price は「shop パッケージの price モジュール」という意味です。
まとめ
- モジュールは、関連する関数・クラスをまとめ、他から import して使える部品。Python・JavaScript では1ファイル、Java では module-info.java でパッケージを束ねた単位
- 大きさは 関数・クラス → モジュール → パッケージ → ライブラリ(Java だけはパッケージ ⊂ モジュール)
- モジュールは 最初の import で1回だけ実行され、2回目以降は保存したものを使う
- 外に見せる仕組みは Python は約束(
_)、JavaScript はexport、Java はexportsで強さが違う - 分け方の物差しは 凝集度は高く、結合度は低く。依存の矢印を一方向にそろえ、モジュール構成図で輪が無いかを見る
- 循環 import は「読み込み途中の名前を使った」ことが原因。名前の取り出しを遅らせるか、共通部分を切り出す
参考・出典
確認日はいずれも 2026年10月17日です。
- Python ドキュメント「6. Modules」:https://docs.python.org/3/tutorial/modules.html
- Python ドキュメント「Programming FAQ」(import の使い方・相互 import):https://docs.python.org/3/faq/programming.html
- Python ドキュメント「What's New In Python 3.13」(Improved error messages):https://docs.python.org/3/whatsnew/3.13.html
- Node.js ドキュメント「Modules: ECMAScript modules」:https://nodejs.org/api/esm.html
- MDN「JavaScript モジュール」:https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules
- OpenJDK「JEP 261: Module System」:https://openjdk.org/jeps/261
- Weblio 辞書「モジュール」(デジタル大辞泉ほか):https://www.weblio.jp/content/モジュール
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「モジュールとは|プログラムでの意味・パッケージとの違いを3言語で実例解説」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。