Top / Python / デコレータ
HTML convert time: 0.021 sec.

Python/デコレータ

Last-modified: 2014-10-19 (日) 00:42:48

クロージャの実験

実験1

  • コード
    def outer():
        a = 123
        print('outer: %s' % locals())
        print("outer a = 0x%x" % id(a))
    
        def inner():
            print('inner: %s' % locals())
            b = 456
            print('inner: %s' % locals())
    
        inner()
    
    outer()
  • 結果
    outer: {'a': 123}
    outer a = 0x251cd40
    inner: {}          ★aは入っていない
    inner: {'b': 456}  ★ローカル変数bが定義(代入)された時に登録

実験2

  • コード
    def outer():
        a = 123
        print('outer: %s' % locals())
        print("outer a = 0x%x" % id(a))
    
        def inner():
            print('inner: %s' % locals())
            print("inner a = 0x%x" % id(a))
            print('inner: %s' % locals())
    
        inner()
    
    outer()
  • 結果
    outer: {'a': 123}
    outer a = 0x1c53d40
    inner: {'a': 123}    ★aが入っている
    inner a = 0x1c53d40  ★outerで定義したaと同じアドレス
    inner: {'a': 123}

実験3

  • コード
    def outer():
        a = 123
        print('outer: %s' % locals())
        print("outer a = 0x%x" % id(a))
    
        def inner():
            print('inner: %s' % locals())
            a = 456
            print("inner a = 0x%x" % id(a))
            print('inner: %s' % locals())
    
        inner()
    
    outer()
  • 結果
    outer: {'a': 123}
    outer a = 0x26d9d40
    inner: {}            ★実験1同様aが入っていない
    inner a = 0x271e140  ★outerのaとは違うアドレス
    inner: {'a': 456}    ★ローカル変数aが定義(代入)された時に登録

実験4

  • コード
    def outer():
        a = 123
        print('outer: %s' % locals())
        print("outer a = 0x%x" % id(a))
    
        def inner():
            print('inner: %s' % locals())
            print("inner a = 0x%x" % id(a))
            a = 456
            print('inner: %s' % locals())
    
        inner()
    
    outer()
  • 結果
    outer: {'a': 123}
    outer a = 0x9c8d40
    inner: {}            ★実験1同様aが入っていない
    Traceback (most recent call last):
      File "test.py", line 14, in <module>
        outer()
      File "test.py", line 12, in outer
        inner()
      File "test.py", line 8, in inner
        print("inner a = 0x%x" % id(a))
    UnboundLocalError: local variable 'a' referenced before assignment  ★a定義前に参照したことを検出

実験5

  • コード
    def outer():
        a = 123
        print('outer: %s' % locals())
        print("outer a = 0x%x" % id(a))
    
        def inner():
            print('inner: %s' % locals())
            print("inner c = 0x%x" % id(c))
            print('inner: %s' % locals())
    
        inner()
    
    outer()
  • 結果
    outer: {'a': 123}
    outer a = 0x2901d40
    inner: {}
    Traceback (most recent call last):
      File "test.py", line 13, in <module>
        outer()
      File "test.py", line 11, in outer
        inner()
      File "test.py", line 8, in inner
        print("inner c = 0x%x" % id(c))
    NameError: global name 'c' is not defined  ★未定義変数参照はNameError

(未定義変数参照時はNameErrorになるので区別している)

考察

  • スコープ = 名前空間 = 名前解決辞書
  • 関数実行時にローカルスコープのインスタンスが作られる
  • 代入を行なうとローカルスコープに変数が定義される
  • 変数参照を行なうと最初にローカルスコープを探して、なければ1つ上のスコープを順々に検索する
    • 上のスコープにあればローカルスコープにその変数を定義する
    • 一番上のグローバルレベルスコープにもなければ未定義エラー(NameError)
  • スコープへの変数追加は逐次行なわれるわけではなく、関数実行時点でコード精査して確定する
  • 下記は関数内でvalに代入しているためローカルスコープに変数定義されるが、
    先に変数参照しているためUnboundLocalErrorになる
    val = 0
    def test():
        print(val)
        val = 100

リンク