LoginSignup
0
4

More than 5 years have passed since last update.

nonlocal一題

Last updated at Posted at 2017-02-25

以下の nonlocal_1.py を実行すると何が起きるでしょうか。
環境は Python 3.6.0とします (多少古いPython3でも変わりません)

nonlocal_1.py
def func():
    nonlocal a
    a = 10

a = 2
func()
print(a)
  1. 2 と表示される
  2. 10 と表示される
  3. ランタイムエラー (RuntimeError) を送出する
  4. それ以外

答えは見えにくいように下の方にあります。

解説

そもそも nonlocal の使う場所が変です。

nonlocalの仕様 によると

nonlocal 文は、列挙された識別子がグローバルを除く一つ外側のスコープで先に束縛された変数を参照するようにします。これは、束縛のデフォルトの動作がまずローカル名前空間を探索するので重要です。この文は、中にあるコードが、グローバル (モジュール) スコープ以外のローカルスコープの外側の変数を再束縛できるようにします。

nonlocal 文で列挙された名前は、 global 文で列挙された名前と違い、外側のスコープですでに存在する束縛を参照しなければなりません (新しい束縛が作られるべきスコープの選択が曖昧さを排除できません)。

よって上記のコードは文法エラーにて死にます。a = 2 を関数の前に置いたって無駄です。

ちなみに以下は動作します。

nonlocal_2.py
def func():
    def func1():
        nonlocal a
        a = 10

    a = 2
    func1()
    print(a)

func()

また、普通ならこんな場所に nonlocal なんて書きませんということで

global_1.py
def func():
    global a
    a = 10

a = 2
func()
print(a)

これは動作します。 global は指定された変数をglobal変数として解釈するのであって、存在する変数の有無は気にしません。

もう少し「使った」感じのする nonlocal の例

nonlocal_3.py
import random

def func():
    called = 0  # func1()が呼ばれた回数を記録したい
    def func1(a):
        nonlocal called
        called += 1
        return a + random.randrange(10)

    tmp = 0
    t = random.randrange(10)
    for i in range(t):
       tmp = func1(tmp)
    print('func1()は{}回呼ばれた。結果は{}'.format(called, tmp))

func()
$ python nonlocal_3.py
func1()は8回呼ばれた。結果は39
$ python nonlocal_3.py
func1()は9回呼ばれた。結果は48
$ python nonlocal_3.py
func1()は1回呼ばれた。結果は8
$ python nonlocal_3.py
func1()は4回呼ばれた。結果は24

(答え)

$ python nonlocal_1.py
  File "nonlocal_1.py", line 2
    nonlocal a
SyntaxError: no binding for nonlocal 'a' found

感想

ちょっと人為的過ぎる例題でした。

0
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
4