(2021-05-25 初稿 - )
はじめに
pythonのスクリプトで、ネストした関数間で変数が共有できなくて困った。
実際には、tkinterを用いたスクリプトで、bind関数を作ったので、関数の中に関数を作るはめになった。
よほどのことがない限り、普通は関数の外に関数を作るけどね。^^;
以下のサイトに詳しい解説があるので参照のこと。多謝
スクリプト
まずは、簡単な例を。
def fn_parent():
count = 0
def fn_child():
print(count)
return fn_child()
fn_parent()
$ python3 ./nonlocal01.py 0
親関数で定義された変数の値参照はOK。
しかし、親関数で定義された変数の値を変更しようとすると。
def fn_parent():
count = 0
def fn_child():
count += 1
print(count)
return fn_child()
fn_parent()
1が返ってくることを期待するが…
$ python3 ./nonlocal02.py Traceback (most recent call last): File "./notlocal01.py", line 9, infn_parent() File "./notlocal01.py", line 7, in fn_parent return fn_child() File "./notlocal01.py", line 5, in fn_child count += 1 UnboundLocalError: local variable 'count' referenced before assignment
上記のように、値を変更しようとすると、定義する前に関数を参照しやがってと怒られる。(-_-;)
そこで、nonlocalを入れることにより、正常に動作し、かつ、値も変えられる。
def fn_parent():
count = 0
def fn_child():
nonlocal count
count += 1
print(count)
return fn_child()
fn_parent()
$ python3 ./nonlocal03.py 1
ちなみに、筆者が作成していたtkinterのスクリプトは以下のとおり。
ディレクトリとファイルの選択をやりたかった。
def ask_dir_file(dirname, filename):
sub = tk.Tk()
sub.withdraw()
sub.title("Input Correct Dirname, Filename")
sub.geometry("400x120")
# function
def deletevalue(event):
#エントリーの中身を削除
editbox1.delete(0, tk.END)
editbox2.delete(0, tk.END)
def ok_get(event):
sub.quit()
def chg_dir(n):
nonlocal dirname, filename
dbase = str_base(dirname)
dnum = str_num(dirname)
dirname = dbase + num_adj(dnum, n)
editbox1.delete(0, tk.END)
editbox1.insert(tk.END, dirname)
fbase = str_base(filename.split('.')[0])
fnum = str_num(filename.split('.')[0])
fext = '.' + filename.split('.')[1]
filename = fbase + num_adj(fnum, 0) + fext
editbox2.delete(0, tk.END)
editbox2.insert(tk.END,filename)
def up_dir(event):
chg_dir(1)
def down_dir(event):
chg_dir(-1)
#ラベル
label1 = tk.Label(sub, text='Directory:')
label2 = tk.Label(sub, text='Filename:')
label1.place(x = 20, y = 20)
label2.place(x = 20, y = 50)
#エントリー
editbox1 = tk.Entry(sub, width=40)
editbox1.insert(tk.END, dirname)
editbox1.place(x=100, y=20)
editbox2 = tk.Entry(sub, width=40)
editbox2.insert(tk.END,filename)
editbox2.place(x=100, y=50)
#ボタン
button1 = tk.Button(sub, text='OK')
button1.bind("", ok_get)
button1.place(x=300, y=80)
button2 = tk.Button(sub, text='Clear')
button2.bind("", deletevalue)
button2.place(x=200, y=80)
# UpキーでDirname変更
sub.bind('', up_dir)
sub.bind('', down_dir)
# Enter でも OK
sub.bind('', ok_get)
sub.deiconify()
sub.mainloop()
sub.withdraw()
return editbox1.get(), editbox2.get()
今回の例は、nonlocalで逃げましたが、本格的には上記サイトにも記述があるように、クラスやジェネレータを使うのが一般的だとか。
また何かわかったら記述するね。