(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で逃げましたが、本格的には上記サイトにも記述があるように、クラスやジェネレータを使うのが一般的だとか。
また何かわかったら記述するね。