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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

5553閲覧

【exec(str)】【from ~ import ...】exec(str)に引数"from ~ import ..." を与える

退会済みユーザー

退会済みユーザー

総合スコア0

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

2クリップ

投稿2018/09/24 06:42

編集2018/09/24 07:31

前提・実現したいこと

別のモジュールの中に記述したリストオブジェクト(名前はf_list)をimportする時、モジュールを引数で指定したいと思い、以下のようなコードを書きました。

$ vim ./test.py def test(module_name, obj_name): exec("from {module_name} import f_list") f_list = eval('f_list') if obj_name in f_list: print(True) else: print(False) if __name__ == '__maiin__': eval(input('>>> ')

発生している問題・エラーメッセージ

ここで、同じディレクトリの./factorial.pyのf_listを./test.pyでimportしてみます。

$ vim ./factorial.py f_list = ['fact'] def fact(n, m): if n == m: return m else: return n * fact(n-1)
$ python ./test.py >>> test("factorial", "fact")

すると以下のようなエラーを吐きます。

Traceback (most recent call last): File "test.py", line 11, in <module> eval(input('>>> ')) File "<string>", line 1, in <module> File "test.py", line 3, in test f_list = eval('f_list') File "<string>", line 1, in <module> NameError: name 'f_list' is not defined $ _

試したこと

print(eval("f_list"))と書き換えた./test.pyは動作します。

$ vim ./test.py def test(module_name): exec(f"from {module_name} import f_list") print(eval('f_list')) if __name__ == '__maiin__': eval(input('>>> ') $ python ./test.py >>> test('factorial') ['fact'] $ _

ですが以下のコードは動きません。

$ vim ./test.py def test(module_name): exec(f"from {module_name} import f_list") f_list = eval('f_list') print(f_list) if __name__ == '__maiin__': eval(input('>>> ') $ python ./test.py >>> test('factorial') Traceback (most recent call last): File "test.py", line 16, in <module> eval(input('>>> ')) File "<string>", line 1, in <module> File "test.py", line 3, in test f_list = eval('f_list') File "<string>", line 1, in <module> NameError: name 'f_list' is not defined $ _

つまるところ

execで文字列を式として評価してimportしたオブジェクトf_listが、関数の中で変数f_listとして使えないという問題に直面しています。f_list = eval('f_list')のように変数名を与えても動きません。しかしprint(eval('f_list')は動くので混乱しています。

補足情報(FW/ツールのバージョンなど)

Mac OS X (El Captian)
Anaconda3-5.2.0 (Python3.6)

LouiS0616👍を押しています

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

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

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

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

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

guest

回答3

0

execやevalはスコープや名前空間が絡むと大変なので、そういう用途では使わないほうが無難です。

単純な例を示します。

hoge.py

python

1f_list = [1,2,3]

python

1>>> def f(): 2... exec("from hoge import f_list") 3... print(f_list) 4... 5>>> f() 6Traceback (most recent call last): 7 File "<stdin>", line 1, in <module> 8 File "<stdin>", line 3, in f 9NameError: name 'f_list' is not defined 10>>> f_list = [4,5,6] 11>>> f() 12[4, 5, 6]

こうなる理由は変数のスコープが関数の定義時に構文に基づいて決定されるからです。

execの引数のstrはただの文字列なので、「f_listがfの中の名前空間に存在する」とは認識されません。結果、f_listはグローバル変数なんだろうな、と認識されてしまいます。実行時にローカルの名前空間にf_listが追加されても探索の対象にならない訳です。

ということを踏まえた上で、次にこの状況ですが、

python

1>>> def f(): 2... exec("from hoge import f_list") 3... print(eval("f_list")) 4... 5>>> f() 6[1, 2, 3]

これが無事に実行されるのはeval自体が実行時の環境で評価を行うので、locals辞書に入っちゃったf_listが見えるから、かな・・・?

python

1>>> def f(): 2... exec("from hoge import f_list") 3... f_list = eval("f_list") 4... print(f_list) 5... 6>>> f() 7Traceback (most recent call last): 8 File "<stdin>", line 1, in <module> 9 File "<stdin>", line 3, in f 10 File "<string>", line 1, in <module> 11NameError: name 'f_list' is not defined

これ不思議ですね。実はこんなのでも失敗します。

python

1>>> def f(): 2... exec("from hoge import f_list") 3... print(eval("f_list")) 4... f_list = None 5... 6>>> f() 7Traceback (most recent call last): 8 File "<stdin>", line 1, in <module> 9 File "<stdin>", line 3, in f 10 File "<string>", line 1, in <module> 11NameError: name 'f_list' is not defined 12>>> f_list = [4,5,6] 13>>> f() 14[4, 5, 6]

f_listに対して代入すると何か壊れるのかな? という感じですが・・・

実はpython2だとこれ動きます。

python

1Python 2.7.6 (default, Oct 26 2016, 20:30:19) 2[GCC 4.8.4] on linux2 3Type "help", "copyright", "credits" or "license" for more information. 4>>> def f(): 5... exec("from hoge import f_list") 6... print(eval("f_list")) 7... f_list = None 8... 9>>> f() 10[1, 2, 3]

あまりちゃんと分析していませんが、たぶんこのへんが絡みます。

python - How does exec work with locals? - Stack Overflow

結論は面倒くさいのでやめましょう、です(投げやりだけど・・・)。

exec, evalはそんな気楽に使える便利関数ではありません。特にexecは素晴らしいハマりポイントをたくさん用意してくれるので、死んでも使いたくない。

投稿2018/09/24 07:36

編集2018/09/24 07:41
hayataka2049

総合スコア30933

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

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

0

おもしろい現象ですね。
このようなときは、ミニマムなコードで状況を再現し、原因を絞り込むのが常套手段です。

エラーの再現

次のようなコードで状況が再現しました。Wandbox

module.py

Python

1answer = 42

main.py

Python

1module_name = 'module' 2 3def main(module_name): 4 exec(f'from {module_name} import answer') 5 print(answer) 6 7main(module_name)

エラーの条件の検討と、解決

一方、main.pyを次のように書いたときには問題なく動作します。Wandbox

Python

1module_name = 'module' 2 3# def main(module_name): 4if True: 5 exec(f'from {module_name} import answer') 6 print(answer) 7 8# main(module_name)

この違いは、execをグローバルスコープで実行するか否かだと考えました。
実際、次のようにグローバル辞書を与えてやると上手く動作します。Wandbox

Python

1module_name = 'module' 2 3def main(module_name): 4 exec(f'from {module_name} import answer', globals()) 5 print(answer) 6 7main(module_name)

グローバル空間の汚染を避ける

もう少し検討した結果、次のように書いた方がより正確であることに気付きました。

Python

1module_name = 'module' 2 3def main(module_name): 4 exec(f'from {module_name} import answer as _answer') 5 answer = locals()['_answer'] # eval('_answer') でも可 6 7 print(answer) 8 9main(module_name) 10print(answer)

実行結果 Wandbox

plain

142 2Traceback (most recent call last): 3 File "prog.py", line 10, in <module> 4 print(answer) 5NameError: name 'answer' is not defined

スコープ外でちゃんとエラーになってくれるところが良さげです。

なお、_answerをいちいち経由しているのにも理由があります。

Python

1module_name = 'module' 2 3def main(module_name): 4 exec(f'from {module_name} import answer') 5 answer = locals()['answer'] 6 7 print(answer) 8 9main(module_name) 10# print(answer)

実行結果 Wandbox

plain

1Traceback (most recent call last): 2 File "prog.py", line 9, in <module> 3 main(module_name) 4 File "prog.py", line 5, in main 5 answer = locals()['answer'] 6KeyError: 'answer'

Pythonインタプリタは関数内の代入を前もって舐めているので、
未初期化状態であるanswerをローカル辞書に登録することを避けるのでしょうね。

あくまで推測の域を出ませんが。

原因

検討の末、判明したエラーの要因は次のとおりです。

  • ローカルな名前空間を持つブロックで
  • execに依って代入やそれに準ずる文を書き
  • それ以降に同名の変数を宣言する場合

こっちはNameError: name 'answer' is not definedを吐きますが、 Wandbox

Python

1def main(): 2 exec('answer = 42') 3 answer = eval('answer') 4 5 return answer 6 7print( 8 main() 9)

こっちは吐かないです。Wandbox

Python

1def main(): 2 exec('_answer = 42') 3 answer = eval('_answer') 4 5 return answer 6 7print( 8 main() 9)

最終的な結論

やはりexecやevalを利用するのは最終手段ですね。

投稿2018/09/24 07:17

編集2018/09/24 08:05
LouiS0616

総合スコア35660

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

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

退会済みユーザー

退会済みユーザー

2018/09/24 07:36

def main(): のように関数を定義する場合、ローカルの変数名のみが見えるということでしょうか。 こちらには書きませんでしたが、確かに関数を定義する方法ではなく、スクリプトとして実行すると上手くいくんですよね。 グローバルスコープについて勉強してみたいと思います。
LouiS0616

2018/09/24 07:44

関数を定義する際、内部の代入を解析する処理が走るようなのです。 例えば、次の二つのコードは、別々のエラーを吐きます。 --- >>> def main(): ... print(num) ... >>> main() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in main NameError: name 'num' is not defined --- >>> def main(): ... print(num) ... num = 0 ... >>> main() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in main UnboundLocalError: local variable 'num' referenced before assignment
LouiS0616

2018/09/24 07:47

ここらへんの話がたいへん面倒くさく、また普通にコードを組む分には意識しなくて良い部分なので、やはりevalやexecは避けるべき、というのが一般的な結論です。
guest

0

ベストアンサー

Python: モジュールを動的にロードする

以下引用
標準ライブラリの importlib を使うのが推奨される方法らしいです。

helloworld.py

Python3

1def greet(): 2 print('Hello, World!') 3

Python3

1import importlib 2module = importlib.import_module('helloworld') 3module.greet() 4module.__name__

出力

Hello, World! 'helloworld'

投稿2018/09/24 07:02

opyon

総合スコア1009

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問