関数がネストされたPythonコードで関数の内側で変更した変数を関数の外側から参照する方法を調べてみた
こんにちは、CX事業本部の若槻です。
関数がネストされたPythonコードで、関数の内側で変更(定義)した変数の値を外側から参照したい場合があります。
例えば以下のコードでは、inner()
の外側(outer()
の内側)でvar = 'Initial Var'
とし、inner()
の内側でvar = 'New Var'
としています。
def outer(): var = 'Initial Var' def inner(): var = 'New Var' return(var) print(inner()) print(var)
この場合outer()
を実行した際の出力は
# 出力 >> python outer() New Var Initial Var
となり、inner()
の内側で行った変更が外側に反映されず、print(inner())
とprint(var)
の出力結果が異なる結果となりました。これは、var
のスコープがinner()
およびouter()
のそれぞれのローカルスコープであるためです。
今回は、このように関数がネストされたPythonコードで、関数の内側で変更(定義)した変数の値を外側から参照する方法(上記のようなコードでprint(var)
の出力をNew Var
とする方法)について調べてみました。
変更された値を関数の外側から参照可能とするパターン
以下の4パターンの方法が確認できました。
outer()
内でvar
を定義、inner()
内およびouter()
内でglobal宣言
inner()
内およびouter()
内でvar
のglobal宣言(global var
)を行う方法。
def outer(): global var var = 'Initial Var' def inner(): global var var = 'New Var' return(var) print(inner()) print(var)
# 出力 New Var New Var
var
のスコープをinner()
およびouter()
でグローバルスコープとすることにより、それぞれの関数内から同じグローバルのvar
を変更・参照させることができます。
参考
global 文は、現在のコードブロック全体で維持される宣言文です。 global 文は、列挙した識別子をグローバル変数として解釈するよう指定することを意味します。 global を使わずにグローバル変数に代入を行うことは不可能ですが、自由変数を使えばその変数をグローバルであると宣言せずにグローバル変数を参照することができます。
outer()
内でvar
を定義、inner()
内でnonlocal宣言
var
に対してinner()
内でnonlocal宣言(nonlocal var
)を行う方法。
def outer(): var = 'Initial Var' def inner(): nonlocal var var = 'New Var' return(var) print(inner()) print(var)
# 出力 New Var New Var
inner()
内でnonlocal宣言をすることにより、inner()
の外側のスコープ(=outer()
のローカルスコープ)のvar
の値を変更させることができます。
参考
nonlocal 文は、列挙された識別子がグローバルを除く一つ外側のスコープで先に束縛された変数を参照するようにします。これは、束縛のデフォルトの動作がまずローカル名前空間を探索するので重要です。この文は、中にあるコードが、グローバル (モジュール) スコープ以外のローカルスコープの外側の変数を再束縛できるようにします。
outer()
外でvar
を定義、inner()
内でglobal宣言
outer()
外でvar
を定義して、inner()
内でvar
のglobal宣言(global var
)を行う方法。
var = 'Initial Var' def outer(): def inner(): global var var = 'New Var' return(var) print(inner()) print(var)
# 出力 New Var New Var
outer()
外で定義されたvar
はグローバルスコープとなります。print(var)
実行時にouter()
はローカルスコープ→グローバルスコープの順でvar
を検索するため、inner()
内で変更されたグローバルスコープのvar
の値がprint(var)
では出力されます。
参考
Python では、関数の中からしか参照されていない変数は暗黙的にグローバルとされます。 関数の本体のどこかで値が変数に代入されたなら、明示的にグローバルであると宣言されない限り、ローカルであるとみなされます。
inner()
内でglobal宣言
inner()
内でvar
のglobal宣言(global var
)を行う方法。outer()
内でvar
の定義は行わない。
def outer(): def inner(): global var var = 'New Var' return(var) print(inner()) print(var)
# 出力 New Var New Var
outer()
はローカルスコープ→グローバルスコープの順でvar
を検索するため、inner()
内で変更されたグローバルスコープのvar
の値がprint(var)
では出力されます。
変更された値を関数の外側から参照できないパターン
参照できないパターンについても記載します。
inner()
内のみでglobal宣言
def outer(): var = 'Initial Var' def inner(): global var var = 'New Var' return(var) print(inner()) print(var)
# 出力 New Var Initial Var
inner()
内でグローバルスコープのvar
が定義されていますが、outer()
内ではローカルスコープのvar
が定義されているため、outer()
内ではローカルスコープのvar
が優先して参照されます。
inner()
内でnonlocal宣言
def outer(): def inner(): nonlocal var var = 'New Var' return(var) print(inner()) print(var)
# 出力 [ERROR] Runtime.UserCodeSyntaxError: Syntax error in module 'module': no binding for nonlocal 'var' found (module.py, line 4)
inner()
内でvar
のnonlocal宣言をしても、関数の外側に変更できるvar
がない場合はエラーとなります。
まとめ
- 関数内で変更された変数を関数外で利用したい場合は関数内でglobal宣言かnonlocal宣言を行う。
- 変数の参照時にはグローバルスコープ→ローカルスコープの順で検索が行われる。
おわりに
今回ご紹介した内容は、outer()
をハンドラー、inner()
をハンドラー内の関数とすれば、AWS LambdaのPythonコードにもそのまま当てはめることができます。参考になれば幸いです。
以上