

あけましておめでとうございます。本年もよろしくお願いします。  
Python3で以下のようなコードを実行したところ、エラーが発生しました。  
実験の趣旨はexecにプログラムを文字列で渡すことです。

[code]

    #! /usr/bin/env python3
    # hash_exec_test.py
    
    def exec_test(s):
        exec(s)
    
    exec_test ('''
    book = {"Apple":[120,15], "Orange":[95,32], "Banana":[60,71]}
    print("↓↓One-Values↓↓")
    print(book["Apple"])
    print()
    keys = [x for x in list(book.keys())]
    print("↓↓All-Keys↓↓")
    print(keys)
    print()
    values = [book[x] for x in list(book.keys())]
    print("↓↓All-Values↓↓")
    print(values)
    ''')
    
[/code]

実行結果は以下のとおりです。

[code]

    $ ./hash_exec_test.py
    ↓↓One-Values↓↓
    [120, 15]
    
    ↓↓All-Keys↓↓
    ['Apple', 'Banana', 'Orange']
    
    Traceback (most recent call last):
      File "./hash_exec_test.py", line 19, in <module>
        ''')
      File "./hash_exec_test.py", line 5, in exec_test
        exec(s)
      File "<string>", line 10, in <module>
      File "<string>", line 10, in <listcomp>
    NameError: name 'book' is not defined
    
[/code]

bookという名前が定義されていない、と言われています。  
しかし、上ではbook["Apple"]という検索が成功していますし、その後でキーを取り出すことはできているのに、最後に値を取り出そうとすると成功しない理由が分かりません。

ちなみに、関数exec_testを介さずに、execに文字列を渡してみます。

[code]

    #! /usr/bin/env python3
    # hash_exec_test.py
    
    exec ('''
    book = {"Apple":[120,15], "Orange":[95,32], "Banana":[60,71]}
    print("↓↓One-Values↓↓")
    print(book["Apple"])
    print()
    keys = [x for x in list(book.keys())]
    print("↓↓All-Keys↓↓")
    print(keys)
    print()
    values = [book[x] for x in list(book.keys())]
    print("↓↓All-Values↓↓")
    print(values)
    ''')
    
[/code]

これだとうまくいきます。

[code]

    $ ./hash_exec_test.py
    ↓↓One-Values↓↓
    [120, 15]
    
    ↓↓All-Keys↓↓
    ['Banana', 'Orange', 'Apple']
    
    ↓↓All-Values↓↓
    [[60, 71], [95, 32], [120, 15]]
    
[/code]

なお、ディクショナリ型bookをグローバル変数にしてみます。

[code]

    #! /usr/bin/env python3
    # hash_exec_test.py
    
    def exec_test(s):
        exec(s)
    
    book = {"Apple":[120,15], "Orange":[95,32], "Banana":[60,71]}
    
    exec_test ('''
    print("↓↓One-Values↓↓")
    print(book["Apple"])
    print()
    keys = [x for x in list(book.keys())]
    print("↓↓All-Keys↓↓")
    print(keys)
    print()
    values = [book[x] for x in list(book.keys())]
    print("↓↓All-Values↓↓")
    print(values)
    ''')
    
[/code]

これでも、うまくいきます。

[code]

    $ ./hash_exec_test.py
    ↓↓One-Values↓↓
    [120, 15]
    
    ↓↓All-Keys↓↓
    ['Orange', 'Apple', 'Banana']
    
    ↓↓All-Values↓↓
    [[95, 32], [120, 15], [60, 71]]
    
[/code]

なぜ一番上のケースで、値のリストを得ようとするところだけNameErrorになるのでしょうか。  
ご教示願います。  
ちなみに環境は以下のとおりです。

[code]

    $ uname -a
    Linux DESKTOP-AHPUUO5 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014 x86_64 x86_64 x86_64 GNU/Linux
    $ python3 -V
    Python 3.5.2
    
[/code]

（2017-01-02 11:51 追記）  
ローカル変数の定義状況を示すためにprint(locals())を加えてみました。

[code]

    #! /usr/bin/env python3
    # hash_exec_test.py
    def exec_test(s):
        exec(s)
    
    exec_test ('''
    book = {"Apple":[120,15], "Orange":[95,32], "Banana":[60,71]}
    print("locals")
    print(locals())
    print()
    print("One-Values:")
    print(book["Apple"])
    print()
    keys = [x for x in list(book.keys())]
    print("All-Keys")
    print(keys)
    print()
    print("locals in the list comprehension")
    values = [locals() for x in list(book.keys())]
    print(values)
    print("All-Values")
    values = [book[x] for x in list(book.keys())]
    print("All-Values")
    print(values)
    ''')
    
[/code]

実行結果は以下のとおりです。

[code]

    $ ./hash_exec_test.py
    locals
    {'s': '\nbook = {"Apple":[120,15], "Orange":[95,32], "Banana":[60,71]}\nprint("locals")\nprint(locals())\nprint()\nprint("One-Values:")\nprint(book["Apple"])\nprint()\nkeys = [x for x in list(book.keys())]\nprint("All-Keys")\nprint(keys)\nprint()\nprint("locals in the list comprehension")\nvalues = [locals() for x in list(book.keys())]\nprint(values)\nprint("All-Values")\nvalues = [book[x] for x in list(book.keys())]\nprint("All-Values")\nprint(values)\n', 'book': {'Banana': [60, 71], 'Orange': [95, 32], 'Apple': [120, 15]}}
    
    One-Values:
    [120, 15]
    
    All-Keys
    ['Banana', 'Orange', 'Apple']
    
    locals in the list comprehension
    [{'x': 'Apple', '.0': <list_iterator object at 0x7f871a502470>}, {'x': 'Apple', '.0': <list_iterator object at 0x7f871a502470>}, {'x': 'Apple', '.0': <list_iterator object at 0x7f871a502470>}]
    All-Values
    Traceback (most recent call last):
      File "./hash_exec_test.py", line 25, in <module>
        ''')
      File "./hash_exec_test.py", line 4, in exec_test
        exec(s)
      File "<string>", line 17, in <module>
      File "<string>", line 17, in <listcomp>
    NameError: name 'book' is not defined
    
[/code]

リスト内包表記の中ではローカル変数bookが定義されていません。  
これが原因なのでしょう。  
ではなぜ

[code]

    keys = [x for x in list(book.keys())]
    
[/code]

は正しく動いたかというと、book.keys()は<list_iterator object at
0x7f871a502470>を介して（？）bookにアクセスできていたということでしょうか。


CC BY-SA 3.0
2018-01-01T00:35:11.687
40647
2018-01-11T09:54:17.483
2018-01-02T02:50:19.443
26673
26673
question
python3
Pythonでexec文字列の中にプログラムを書くとNameErrorになる