質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
import

自身のプラットフォーム・プログラム・データセットに対して、外部ソースを取り込むプロセスをimportと呼びます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

10386閲覧

外部のファイルからimportすることによってpythonの循環参照起こらなくなる理由についてご教授ください

yuki8891

総合スコア9

import

自身のプラットフォーム・プログラム・データセットに対して、外部ソースを取り込むプロセスをimportと呼びます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

2クリップ

投稿2020/04/07 14:01

編集2020/04/07 14:06

わかりづらいタイトルですみません。
pythonのimportの挙動に関して感覚的にわからない点があったため、お分かりになる方がいらっしゃればお教えいただきたいです。

あるフォルダに以下の3つのファイルがあります。

  • main.py
  • obj.py
  • load_obj_from_main.py

各ファイルの中身は以下です。

main.py

1 print('main.py started') 2 var = 'this is var' 3 4 print('main.py attempts to import Obj') 5 from obj import Obj 6 print('main.py finished')

obj.py

1 print('obj.py started') 2 from main import var 3 print(var) 4 5 class Obj: 6 pass 7 8 print('obj.py finished')

まずはここで
python3 main.py
を実行します。

実行結果は以下のように、循環参照でエラーが発生します

main.py started main.py attempts to import Obj obj.py started main.py started main.py attempts to import Obj Traceback (most recent call last): File "main.py", line 5, in <module> from obj import Obj File "/home/yuki/.___tmp____/circle_import/obj.py", line 2, in <module> from main import var File "/home/yuki/.___tmp____/circle_import/main.py", line 5, in <module> from obj import Obj ImportError: cannot import name Obj

ここで、load_obj_from_main.pyを準備し、以下のように書きます。

load_obj_from_main.py

1 from main import Obj 2 3 print(vars(Obj))

python3 load_obj_from_main.py
を実行します。
出力は以下のようになります。

main.py started main.py attempts to import Obj obj.py started this is var obj.py finished main.py finished {'__module__': 'obj', '__dict__': <attribute '__dict__' of 'Obj' objects>, '__weakref__': <attribute '__weakref__' of 'Obj' objects>, '__doc__': None}

循環参照が発生せずに、ちゃんとObjが出力されています。
感覚的には、直接main.pyを実行した時と同じように、main.pyの読み込みが完了していないため、varの準備ができておらず、再度main.pyを読み込みに行ってobj.pyのObjを再度読もうとし、未定義のためimportエラーが発生するかと思ったのですがしっかりvarを読み込めています。

この直接main.pyを読んだ時と違って間接的に(?)読み込んだ時はなぜmain.pyのvarを取得できているのでしょうか。
python初心者で未熟者のためなぜこのような挙動になっているのか理解できておりません。

皆様にご教授いただけますと幸いです。

Lhankor_Mhy👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

これは循環インポートだからエラーになっているのではないのです。

正しくは
mainを先にインポートするとエラーにならないけれども、objを先にインポートするとエラーになる
と解釈するべきです。


python

1import obj

という1行だけの実行スクリプトを実行してみればImportError: cannot import name Objという同様のエラーがでることが確認できるはずです。
また

python

1import main

であれば

plain

1main.py started 2main.py attempts to import Obj 3obj.py started 4this is var 5obj.py finished 6main.py finished

という結果を見ることができます。

main内ではvarが名前空間に束縛されたにobjをインポートしています。objの中でmainがインポートされた時にはmainの名前空間にvarがあるので、mainを先にインポートするとエラーになりません。

obj内ではObjが名前空間に束縛されるにmainをインポートしています。mainの中でobjがインポートされた時にはobjの名前空間にObjがないので、objを先にインポートするとObjが取り出せずにエラーになります。


では、なぜpython main.pyとするとエラーが起きるのかという話です。

pythonの引数に指定したファイルやコード(トップレベルのコードと呼ばれる様ですが)は、__main__というモジュール名で束縛されるからです。
https://docs.python.org/ja/3/library/main.html

python main.pyとすると「main.pyの内容を使って__main__というモジュールがロードされる」のであって、mainというモジュールがロードされるのではないのです。
その中でまずobjをインポートしていますから「objを先にインポートしている」ことになってエラーが起きています。

python load_obj_from_main.pyでは正しく「mainを先にインポートしている」のでエラーが起きません。

投稿2020/04/08 00:27

編集2020/04/08 00:44
quickquip

総合スコア11235

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuki8891

2020/04/12 22:06

お礼が遅くなってしまい申し訳ありません。とてもわかりやすい解説で感動しました。 実際にこの問題は、実際に構築していてずっと謎だったことでして、今まで同様の問題が起きると理由もよくわかっていないまま、別のファイルからimportするという対症療法でしのいでいたのですが、ずっと理解できずにモヤモヤしていました。 python main.pyとすると「main.pyの内容を使って__main__というモジュールがロードされる」のであって、mainというモジュールがロードされるのではないのです。 これをお教えくださったおかげでとてもよく理解できました。大変助かりました。 誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問