この記事でわかること
Thank you for reading this post, don't forget to subscribe!– Pythonの相対インポート `from .module` と `from module` の違いがなぜ「クソ仕様」なのか
– たった1文字 `.` の有無でプログラムの生死が分かれる恐怖
– Pythonがこの「目障りな仕様」を変えられない本当の理由
– AS/400の思想と比較してわかる、Python設計の「後先考えなかった」証拠

「なんで `.` がついただけで動かなくなるんだよ!」
これは私が昨日、WSL2上のPythonバッチ処理で3時間悩んだ末に出た言葉です。
私は今、DeepSeek-R1を制御するエンタープライズ級のバッチ処理をPythonで書いています。シェルスクリプトでは限界があったからです。しかし――
Pythonを知ってまだ1週間。そして「この仕様」を知ったのが昨日。
結論から言います。Pythonの開発者が存命なら申し訳ないですが、これは「後先考えなかった」と断言せざるを得ない。なぜなら、たった1文字 `.` の有無でプログラムの生死が分かれる設計は、もはや「虐待」だからです。
しかも、同じような「1文字地獄」がPythonには山ほどある。
今回は、Pythonの最も分かりにくい仕様のひとつ「相対インポート」 にスポットを当て、なぜこれが生き残り、なぜ変えられないのかを深掘りします。

◆ 第1章:私が遭遇した「悪夢の3時間」
状況再現
私は以下のようなディレクトリ構成でバッチ処理を書いていました。
G:/@Ubuntu/CLP/PGM-N/
├── main.py # エントリポイント
├── config.py # 設定読み込み
├── logger.py # ログ出力
├── ocr.py # OCR実行
├── extract.py # テキスト抽出
├── preprocess.py # 前処理
├── split_chunks.py # チャンク分割 ← ここでエラー
├── translate.py # 翻訳呼び出し
├── dictionary.py # 辞書適用
├── fileops.py # ファイル操作
└── mailer.py # メール通知
この中の `split_chunks.py` から、同じディレクトリにある `logger.py` の関数を呼びたくなりました。(各工程の進捗都度Loggingしたかった。長時間処理の為、進捗を知りたかった!)
私がやった間違い
# split_chunks.py の先頭に書いたコード
from logger import log_info # ← これだと動かない!
出てきたエラー
ModuleNotFoundError: No module named 'logger'
これで、何がいけないのか直ぐに判ったあなたは、「python中毒」間違いなく
正解はこれだった
from .logger import log_info # ← 先頭に「.」を入れるだけで動く
“.“こんなの分かる訳がない。pythonを知って1週間足らず、そして触ったのが昨日(1日前)・・・たった1文字(しかも小さい.)思わず「頭大丈夫!?開発者」と、罵ってしまった・・・これは嘘だけど、正にその様な気持ちになった。
たった1文字。ドットひとつ。
しかも、`main.py` から呼ぶときは `.` が不要なんです。
# main.py ではこれで動く
from logger import log_info
同じコードなのに、呼び出す場所によって `.` が必要になったりならなかったり。
――は? 頭おかしくない?
この発見に至るまで、私は3時間を無駄にしました。ググって(正確にはGeminiに聞いて)、StackOverflowを読み漁って、やっと「そういう仕様なんだ」と納得するまで。

◆ 第2章:なぜこんな「分かりにくい仕様」になったのか?
2-1. 私が抱いた疑問(そしてあなたも抱くはず)
ここで、私が3時間悩んだ末に抱いた疑問をそのまま書きます。
> 「他にも `.` 1文字だけの違いで切り分ける定義があるのだろうか? 目が悪いと見過ごしてしまいそうな・目立たない `.` にした? もっと工夫のしようがなかったのか? はなはだ疑問に感じるのは私だけだろうか? センスがないとも思える。」
・・・いやハッキリ言う「センス無しだ。後先考えていない。考えていなかったと断言する。」
この疑問、私だけじゃありません。世界中のPython初心者が同じことを思います。キット
では、なぜPythonはこんな仕様になったのか。歴史を追いましょう。
2-2. Pythonのインポートは「歴史的負債の塊」だった
ステップ1:Pythonは「小さいもの」として生まれた
1991年、Guido van RossumがPythonを作ったとき――
大規模プロジェクトなんて想定していなかった。
当時は:
– パッケージ(フォルダ単位の管理)という概念がなかった
– 全部の `.py` ファイルを同じフォルダに置くのが普通
– インポートは「現在のフォルダ+システム全体」を適当に探すだけ
つまり、最初から「バカでも使える簡易言語」だった。👈言い方が悪いが、後先のことを考えていなかった証拠です。
ステップ2:プロジェクトが巨大化して問題発生
2000年代に入り、Pythonが普及するにつれて問題が表面化した。
「同じ名前のファイルが大量に存在する」
例えば:
– `utils.py` というファイルが10個のフォルダに存在
– どの `utils.py` をインポートするかPythonは判断できない
そこで後付けで追加されたのが 「相対インポート(`.module` の文法)」。
ステップ3:後付けだから「互換性」が最優先された
ここが最も重要なポイント――
既に世界中で何百万行ものPythonコードが動いていた。
新しい文法を追加すると:
– 既存コードが動かなくなる
– 全世界のプロジェクトが壊れる
– Pythonは終わる
だからGuidoは 「最小限の変更」 を選んだ。
> 「既存の `from logger import` はそのままにして、 `from .logger import` という新文法を追加しよう」
――これが、たった1文字 `.` で挙動が変わる「クソ仕様」の誕生です。
2-3. なぜ `.` という文字を選んだのか?(UNIXの悪影響)
ここでまた疑問が湧きます。
> 「なぜ `.` なんだ? もっと目立つ文字(@や#や>)にすればよかったのに。」
その答えは「UNIXの伝統をそのまま流用した」からです。
UNIXでは:
– `.` = カレントディレクトリ
– `..` = 親ディレクトリ
Pythonの開発者はこの記号をそのままインポートに持ち込みました。
つまり:
– `.logger` → 「このフォルダのlogger.py」
– `..utils` → 「1つ上のフォルダのutils.py」
しかし――UNIXの`.`は「コマンド実行時のカレントディレクトリ」であって「インポートのスコープ指定」ではありません。
異なる概念を無理やり同じ記号で表現した時点で混乱は必至でした。
しかも、後付け機能だったので「目立たせる余裕がなかった」。既存のコードを壊さないために、最小限の変更にせざるを得なかった――これが全ての元凶です。

◆ 第3章:AS/400と比較してわかる「Pythonの設計限界」
ここで、私が以前からリスペクトしているAS/400の思想と比較してみましょう。
(私が、数十年に渡って使用して来たSystem/38から)
AS/400のLIBL(ライブラリリスト)は――
– 明示的
– 階層が固定(正確には階層ではない*LIBL(LibraryのList順))
– 参照順序が変わらない
– 同名オブジェクトでも属性で区別(オブジェクト属性が異なれば、同じLibraryに配置できる。)
– 誤参照が起きないように設計されている(同じObject(同じ属性が)別のLibraryに存在しても、*LIBL順に、更新・参照:だからテスト環境の整備がとても柔軟で楽CHGLIBL LIBL(TestLib MainLib)で、Testに存在しないObjectは、MainのObejectを利用・参照(誤ってMainのObjectを更新させない様に指定できる。))
つまり、「ヒューマンエラーが起きないように設計されている」 のです。
対してPythonのインポートは――
– 暗黙的
– sys.pathの順序に依存
– OSのディレクトリ列挙順に依存
– 同名ファイルがあれば競合
– 誤参照が普通に起きる
– しかも `.` という1文字で挙動が変わる
これは「エラーが起きるように設計されている」とすら言えます。
「急がば回れ」という言葉があります。
事前に設計をちゃんとして、後で困らないようにする――これがプロのやり方。
Pythonのインポート機構は 「急がば回れ」の真逆。
「とりあえず後で何とかする」で作った結果が、30年経っても修正されずに残っている。
AS/400の思想から見れば、Pythonのインポートは「危険で曖昧で分かりにくい」という評価は当然です。

◆ 第4章:他にもある「1文字地獄」の数々
「他にも `.` 1文字だけの違いで切り分ける定義があるのだろうか?」
ある。しかも大量にある。
地獄その1:`=` と `==`
```python
# 代入(値が変わる)
a = 10
# 比較(True/Falseを返す)
a == 10
```
たった1文字違いで、処理が完全に変わる。
バグの温床ランキング常連。私も昨日これでハマりました。
地獄その2:`is` と `==`
```python
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True(値が同じ)
a is b # False(別のオブジェクト)
```
「オブジェクトの同一性」という概念。初心者に理解させるのは至難の業。私もまだ完全には理解できていません。
地獄その3:`*args` と `**kwargs`
```python
def func(*args): # タプルで受け取る
pass
def func(**kwargs): # 辞書で受け取る
pass
```
`*`(アスタリスク)が1つか2つかで意味が変わる。
しかも、これらは併用できます。
```python
def func(*args, **kwargs):
pass
```
「もういい、頭が爆発しそうだ」――これが私の正直な気持ちです。
地獄その4:`.` と `..` の相対インポート
そして極めつけがこれ。
```python
from .logger import log_info # 同じフォルダ
from ..logger import log_info # 1つ上のフォルダ
```
たった1文字のドットの数で、参照先が変わる。
目が悪いと見逃す。私の目のせいじゃない。設計が悪いんです。

◆ 第5章:なぜPythonはこの「クソ仕様」で生き残ったのか?
ここが一番不思議なポイント。
こんなに分かりにくい仕様なのに、なぜPythonは世界で最も使われる言語のひとつになったのか?
理由1:シンプルさの「見せかけ」が初心者を騙した
「Hello World」が1行で書ける。
リスト操作が直感的。
インデントでブロックを表現。
――初心者は「簡単だ!」と思って飛びつく。
しかし、そこから先は地獄の入り口。私がまさにその状態です。
理由2:データサイエンス・AIブームに乗った
Pythonがここまで使われるようになった最大の理由は――
「他に選択肢がなかったから」。
データサイエンティストはプログラマーじゃない。仕方なくPythonを使っている。
「インポートの仕様が分かりにくい」なんて問題は、データ分析には関係ないから放置された。
理由3:コミュニティが「仕様は仕様」と受け入れた
Pythonコミュニティには 「仕様がクソでも、それがPythonの特徴」 という謎のカルチャーがある。
「なぜ `.` なのか?」と質問すると:
> 「それはPythonの仕様だから。覚えなさい。」
――これが答え。
AS/400の思想から見れば絶対にありえない。
AS/400では「分かりにくい」は容認されない。
なぜならエンタープライズでは「ヒューマンエラーを減らす」が最優先だから。
(私は、ミッションクリティカルの世界で育ち生きてきた。)
対してPythonは 「個人の自己責任」 の文化。
当然、「個人の自己責任」 の世界観も必要だし、否定している訳ではない。
(人は「ポカをする。」世界の冠たるトヨタ自動車の生産管理システムには「ポカよけの仕組みがある」)なぜ、ITの世界で「ポカよけ」しない?
ことわざにも「転ばぬ先の杖」がある。

英語にも「Haste makes wasteなど」がある。「急ぐと結果的に遅くなる」という、人生の普遍的な真理(ことわざ)を語るフレーズ。Walk, don’t run.「今は落ち着いて、一歩ずつ進むときだ」と、目の前の行動に対して具体的にブレーキをかけるフレーズ。
◆ 第6章:「急がば回れ」――私がこの経験から学んだこと
「急がば回れ」
この言葉を私は大事にしています。
今回の経験で痛感したのは――
Pythonの設計者は「急がば回れ」を無視した。
「とりあえず動けばいい」「後で何とかする」で作った結果が30年経っても修正されない。
これに対して、私が作ろうとしているバッチ処理では「急がば回れ」を徹底する。
なぜなら、DeepSeek-R1(cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-Q5_K_M.gguf)の暴走に疲れたから。シェルスクリプト(.sh)では限界だったから。Qwen2.5-14B-Instruct-Q5_K_M.ggufに替え、だからPythonを選んだのに、この始末。
でも、愚痴を言っても始まらない。
私が実践する「対策3選」
:明示的な絶対インポートを使う
```python
# 悪い例(混乱する)
from .logger import log_info
# 良い例(明確)
from PGM-N.logger import log_info
```
対策2:パッケージ化してしまう
`setup.py` を作ってインストール可能なパッケージにする。
```python
# インストール後はこれでOK
from logger import log_info
```
対策3:全部のインポートをmain.pyだけに集約する
いわゆる「ファサードパターン」。
`main.py` だけが他のモジュールをインポートし、それ以外のモジュールは `main.py` 経由でしか呼ばない。
私が選んだのは対策3。
だって、バッチ処理なら構造は単純でいい。複雑にするとバグるから。これこそ「急がば回れ」の精神です。

◆ 第7章:結論――Pythonは「クソ仕様」だけど、それでも選ばれる理由
ここで、3時間悩んだ私の言葉をそのまま書きます。
> 「パイソンの開発者が存命なら申し訳ないが、なぜクソ急いだ、後先考えなかった。先頭の `.` たった1文字で挙動が変わる仕様なんて、見分けがつかないよ。」
まったくその通り。
Pythonのインポート設計は 「後先考えなかった」 の一言に尽きる。
しかし――
それでもPythonは生き残り、世界中で使われている。
その理由は:
1. 「最初のハードルが異常に低い」 から
2. 「データサイエンス・AIという巨大な波に乗った」 から
3. 「コミュニティが巨大で、どんな問題もググれば解決策が出てくる」 から
つまり、「言語そのものの完成度」ではなく「エコシステムの強さ」 で勝っている。
これは、AS/400のような「完成された思想を持つシステム」とは真逆の価値観。
そして私は――
Pythonを選んだことを後悔していない。いや、正確に言えば「後悔しているが、他に選択肢がない」。
ローカルPC(LMStudioじゃない、WSL2だ)でバッチのジョブを作り始めて早1カ月。DeepSeek-R1の暴走に疲れ、シェルスクリプトじゃあどうにもならないと、Qwen2.5-14B-Instruct-Q5_K_M.ggufに替え、Pythonを選んだ。
実は、Pythonを知ってまだ1週間に届かない。そしてPythonの「クソ具合」を知ったのが昨日。
それでも、私はこの言語と付き合っていく。
だって、AIオーケストレーションの最適解が、今のところPythonだから。

◆ おわりに――同じ罠にはまったあなたへ
Pythonを知ってまだ1週間――
それでいて「この仕様はおかしい」と感じたあなた(そして私)の感覚は、まったく正しい。
むしろ、長年Pythonを触っている人ほど「仕様は仕様」と麻痺している。
「急がば回れ」――この言葉を忘れずに。
Pythonの `import` システムは「急いで作って後でひどい目を見た」典型例。
あなたが作っているバッチ処理では、同じ過ちを繰り返さないでほしい。
なお、このブログを書き終えた今も、私のDeepSeekバッチは相変わらず暴走している。今度はメモリリークらしい。(<Thinking>が止まらない。制御できない。だから、Qwen2.5-14B-Instruct-Q5_K_M.ggufに)
Pythonを選んだことを後悔しつつ、でも他に選択肢がないので、これからデバッグを続けます。
あなたのバッチ処理が無事に完成することを、同じ罠にはまった者として心から願っています。

Python(パイソン)の概要と特徴
AI開発やデータ分析、Webシステム、自動化スクリプトまで幅広く使われる世界的人気のプログラミング言語です。
💡 主な特徴
- シンプルな構文:読みやすさを重視した設計(インデントによるブロック表現)。
- 圧倒的なライブラリ量:機械学習(AI)からデータ処理、スクリプト作成まで部品が豊富。
🚀 独自の優位点
- 開発効率の高さ:少ない行数で記述でき、初心者からプロまで直感的にコードが書ける。
- 強力なコミュニティ:世界中に開発者が多く、エラーの解決策や情報がネット上に溢れている。
⚠️ 知っておくべき弱点(落とし穴)
- 実行速度が遅い:インタプリタ言語(逐次実行)のため、C言語等に比べ処理速度が劣る。
- インポートの曖昧さ:外部モジュールを読み込む際、「現在のフォルダ + システム全体(環境変数)」を上から適当に探すだけの仕様。自作ファイルと同名の有名ライブラリ(例:
math.pyやjson.pyなど)を同じフォルダに作ると、名前が衝突して読み込みエラー(シャドーイング)を起こす。 - バージョンと環境管理の複雑さ:グローバル環境を汚しやすく、プロジェクトごとに仮想環境(venvなど)を作って管理しないと、ライブラリの依存関係が崩壊しやすい。
◆ 参考:今回のブログで書いた「Pythonクソ仕様」チェックリスト
– [x] 相対インポートの `.` 問題
– [x] `=` と `==` の違い
– [x] `is` と `==` の違い
– [x] `*args` と `**kwargs` の違い
– [x] AS/400との比較
– [x] 「急がば回れ」対「とりあえず実装」
– [x] 3時間溶かした実体験 **あなたの「これもクソだ」という声、ぜひコメントで聞かせてください。


