Pythonプログラミングイントロダクション(4) 静的スコープと動的スコープの違い

2019–11–03

4章をみていきます。この章ではプログラムを書く上で避けては通れない関数、スコープ、抽象化についてです。

プログラミングにおいては処理をひたすら書いていくだけではなく、共通の処理は関数として切り出したり、変数のスコープを意識して変数が見える範囲を理解する必要があります。 スコープを理解していないと処理の対象がなんなのか、どこまで参照可能なのかを分からずにプログラミングすることになり、効率が悪くなります。

静的スコープ

Pythonは静的スコープ(レキシカルスコープ)の言語です。本書で出てくるスコープに関連する用語を整理しておきます。

名前空間 違う名前空間では同じ名前の変数が異なるものとして扱われる。このように分割して衝突を回避する概念
スコープ 変数や関数を参照できる範囲のこと
局所変数 ローカル変数ともいう。関数内で利用できる変数のこと。
大域変数 グローバル変数ともいう。全てのスコープから利用できる変数のこと

同じ名前の変数 x をプログラム内で使用した場合、それぞれの関数ではどの x が参照されているか見ていきます。

def hoge():
    def fuga():
        x = 'fuga'
        print('fuga print x: ' + x)

    def moga():
        print('moga print x: ' + x)


    x = 'hoge'
    print('hoge print x: ' + x)
    fuga()
    moga()
    foo()

def foo():
    print('foo print x: ' + x)


x = 'foo'
hoge()	  

出力は下記のようになります。

: hoge print x: hoge
: fuga print x: fuga
: moga print x: hoge
: foo print x: foo	  

下記は全て違うものになります。

  • globalで定義された x
  • hoge関数で定義された x
  • fuga関数で定義された x

下記は同じものになります。

  • hoge関数で定義された x とmoga関数で参照された x
  • globalで定義された x とfoo関数で参照された x

このように、関数がどこから呼び出されたかに関係なく、どこに定義されているかで変数のスコープが変わるのが静的スコープになります。 また、Pythonではlocal変数やグローバル変数をlocals()関数やglobals()関数で表示させることができます。

def hoge():
    def fuga():
        x = 'fuga'
        print('fuga locals: ', locals())

    def moga():
        print('moga locals: ', locals())


    x = 'hoge'
    fuga()
    moga()
    foo()

    print('hoge locals: ', locals())

def foo():
    print('foo locals: ', locals())

x = 'foo'
hoge()
print('globals: ', globals())	  

結果は下記のようになります。同じ変数名 x でfuga, hoge, globalに定義がされていることがわかります。

: fuga locals:  {'x': 'fuga'}
: moga locals:  {}
: foo locals:  {}
: hoge locals:  {'fuga': <function hoge.<locals>.fuga at 0x1014db440>, 'moga': <function hoge.<locals>.moga at 0x1014db4d0>, 'x': 'hoge'}
: globals:  {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '<stdin>', '__cached__': None, 'hoge': <function hoge at 0x1014db320>, 'foo': <function foo at 0x1014db3b0>, 'x': 'foo'}	  

動的スコープ

静的があれば動的もあります。関数がどこから呼び出されたかで変数のスコープが変わるのが動的スコープになります。 bashで変数にlocalをつけると動的スコープになります。

#!/bin/bash
x="hoge"

hoge() {
    echo "in hoge x is $x"
}

foo() {
    local x="foo"
    hoge
}

foo
hoge	  
in hoge x is foo
in hoge x is hoge	  

foo関数からhoge関数が呼ばれていますが、hoge関数で参照される x はfoo関数が呼び出す直前に設定された x になっています。

まとめ

本書で説明されている静的スコープと対比して、動的スコープがどうなっているかを bashのlocal変数の例で整理してみました。 本書ではlocals()関数で表示されるシンボルテーブルの取り扱いをわかりやすい図で説明してくれています。是非こちらも参照してみてください。