はじめに
動機:関数内でグローバル変数の動的な定義&値の代入がしたい!
(こんなことホイホイとやっちゃダメですが…。)
def dainyuu(val_name):
global val
val = 123 # ここで指定された名前の変数に代入したい
dainyuu("val")
print(val) # 関数外で参照
exec()
によって動的な変数の定義や代入が可能ですが、グローバル変数への代入で少し手間取ったのでまとめました。
なお、検証は Python 3.7.3 です。
- 公式リファレンス:組み込み関数#exec
パターン1:グローバル変数のシンプルな定義
-
exec
の第2引数にglobals()
を渡す - 第3引数を渡さない
exec
に globals()
のみを渡すことによって、文字列中の代入はグローバル変数への代入になります。
# 特に指定がない場合、ローカル変数の定義になる
def define_variable_test1(var_name):
exec("{0} = 123".format(var_name))
eval("print('{0}(in) : ' + str({0}))".format(var_name))
# globals() を渡すとグローバル変数の定義になる
def define_variable_test2(var_name):
exec("{0} = 345".format(var_name), globals())
eval("print('{0}(in) : ' + str({0}))".format(var_name))
define_variable_test1("val1")
try:
print("val1(out): {0}\n".format(val1))
except NameError:
print("val1(out): not defined\n")
define_variable_test2("val2")
try:
print("val2(out): {0}\n".format(val2))
except NameError:
print("val2(out): not defined\n")
val1(in) : 123
val1(out): not defined
val2(in) : 345
val2(out): 345
パターン2:グローバル変数の定義にローカル変数を使う
-
exec
にglobals()
とlocals()
を渡す - 評価文中で
global
変数宣言を行う
exec
に locals()
を渡すと文字列中の代入はローカル変数になりますが、同じ文字列中で global
宣言を行っておくことでグローバル変数への代入が行えます。
# locals() も渡すとローカル変数の定義になってしまう
def define_variable_test3(var_name):
local_val = 12345
exec("{0} = local_val".format(var_name), globals(), locals())
eval("print('{0}(in) : ' + str({0}))".format(var_name))
# exec 内でのグローバル変数宣言によってグローバル変数の定義になる
def define_variable_test4(var_name):
local_val = 34567
exec("global {0}\n{0} = local_val".format(var_name), globals(), locals())
eval("print('{0}(in) : ' + str({0}))".format(var_name))
define_variable_test3("val3")
try:
print("val3(out): {0}\n".format(val3))
except NameError:
print("val3(out): not defined\n")
define_variable_test4("val4")
try:
print("val4(out): {0}\n".format(val4))
except NameError:
print("val4(out): not defined\n")
val3(in) : 12345
val3(out): not defined
val4(in) : 34567
val4(out): 34567
当たり前と言えば当たり前ですが、引数として global()
とかを渡せるのもあって無駄に悩みました…
というか、こんな邪道を歩む行為やめようねって話です。
パターン3:globals()
の返り値を直接編集する
コメントに寄せて頂いた情報です。
く、黒魔術だ…!
# globals() で返る辞書を直接編集してしまうことも可能
def define_variable_test5(var_name):
local_val = 1234567
globals()[var_name] = local_val
eval("print('{0}(in) : ' + str({0}))".format(var_name))
define_variable_test5("val5")
try:
print("val5(out): {0}\n".format(val5))
except NameError:
print("val5(out): not defined\n")
val5(in) : 1234567
val5(out): 1234567
普通に代入が成功してますが、正直言ってあまり推奨される行為ではないと思うのでご注意ください。
将来的に動作が変わる可能性もあります。
なお、globals()
では成功しましたが、locals()
では関数内での代入は失敗します。(実際のローカル変数に変更が反映されない)
注釈: この辞書の内容は変更してはいけません; 変更しても、インタプリタが使うローカル変数や自由変数の値には影響しません。
def changing_local_variable_test():
var = 123
print("before: {0}".format(var))
locals()["var"] = 234
print("after: {0}".format(var))
changing_local_variable_test()
before: 123
after: 123