5

tkinter での GUI プログラミングにラムダ関数を使用しています。最近、ファイルを開くボタンを実装するときに行き詰まりました。

self.file=""
button = Button(conf_f, text="Tools opt.",
        command=lambda: tktb.helpers.openfile(self.file))

ご覧のとおり、更新可能なファイル パスを定義したいのですが、GUI の作成時には不明です。私が抱えていた問題は、以前のコードが次のとおりだったことです。

button = Button(conf_f, text="Tools opt.",
        command=lambda f=self.file: tktb.helpers.openfile(f))

ラムダ関数には、ファイル パスを渡すためのキーワード引数がありました。この場合、パラメーターfは更新されたときに更新されませんself.fileでした。

コード スニペットからキーワード引数を取得し、どこでも使用しています。明らかに私はすべきではありません...

これはまだ私には明確ではありません...誰かが2つのラムダ形式の違いと、いつ互いに使用するかを説明してもらえますか?

ありがとうございました!

PS:次のコメントは私を解決策に導きましたが、もう少し説明が欲しいです: ラムダはtkinterで奇妙に動作します

4

1 に答える 1

10

もっと詳しく説明しようと思います。

もしあなたがそうするなら

i = 0
f = lambda: i

i囲んでいるスコープの変数にアクセスする関数(ラムダは本質的に関数です)を作成します。

内部的には、i. 大まかに言えば、異なる時点で異なる値を保持できる実数変数へのポインターのようなものです。

def a():
    # first, yield a function to access i
    yield lambda: i
    # now, set i to different values successively
    for i in range(100): yield

g = a() # create generator
f = next(g) # get the function
f() # -> error as i is not set yet
next(g)
f() # -> 0
next(g)
f() # -> 1
# and so on
f.func_closure # -> an object stemming from the local scope of a()
f.func_closure[0].cell_contents # -> the current value of this variable

ここで、 のすべての値はi、その時点で、そのクロージャーに格納されます。関数f()がそれらを必要とする場合。そこからそれらを取得します。

逆アセンブリ リストでその違いを確認できます。

これらは機能a()を述べ、f()次のように逆アセンブルします。

>>> dis.dis(a)
  2           0 LOAD_CLOSURE             0 (i)
              3 BUILD_TUPLE              1
              6 LOAD_CONST               1 (<code object <lambda> at 0xb72ea650, file "<stdin>", line 2>)
              9 MAKE_CLOSURE             0
             12 YIELD_VALUE
             13 POP_TOP

  3          14 SETUP_LOOP              25 (to 42)
             17 LOAD_GLOBAL              0 (range)
             20 LOAD_CONST               2 (100)
             23 CALL_FUNCTION            1
             26 GET_ITER
        >>   27 FOR_ITER                11 (to 41)
             30 STORE_DEREF              0 (i)
             33 LOAD_CONST               0 (None)
             36 YIELD_VALUE
             37 POP_TOP
             38 JUMP_ABSOLUTE           27
        >>   41 POP_BLOCK
        >>   42 LOAD_CONST               0 (None)
             45 RETURN_VALUE
>>> dis.dis(f)
  2           0 LOAD_DEREF               0 (i)
              3 RETURN_VALUE

b()それを次のような関数と比較してください

>>> def b():
...   for i in range(100): yield
>>> dis.dis(b)
  2           0 SETUP_LOOP              25 (to 28)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (100)
              9 CALL_FUNCTION            1
             12 GET_ITER
        >>   13 FOR_ITER                11 (to 27)
             16 STORE_FAST               0 (i)
             19 LOAD_CONST               0 (None)
             22 YIELD_VALUE
             23 POP_TOP
             24 JUMP_ABSOLUTE           13
        >>   27 POP_BLOCK
        >>   28 LOAD_CONST               0 (None)
             31 RETURN_VALUE

ループの主な違いは

        >>   13 FOR_ITER                11 (to 27)
             16 STORE_FAST               0 (i)

b()対で

        >>   27 FOR_ITER                11 (to 41)
             30 STORE_DEREF              0 (i)

in a():オブジェクト (クロージャ)STORE_DEREF内のストア。(おそらく) 少し高速に動作する「通常の」変数を使用します。cellSTORE_FAST

ラムダも違いを生みます:

>>> dis.dis(lambda: i)
  1           0 LOAD_GLOBAL              0 (i)
              3 RETURN_VALUE

ここには がありますが、LOAD_GLOBAL上記のものは を使用していますLOAD_DEREF。後者も閉鎖用です。

のことをすっかり忘れていましlambda i=i: iた。

値をデフォルト パラメータとして持っている場合は、完全に異なるパスを介して関数への道を見つけます。現在の値はi、デフォルト パラメータを介して、作成したばかりの関数に渡されます。

>>> i = 42
>>> f = lambda i=i: i
>>> dis.dis(f)
  1           0 LOAD_FAST                0 (i)
              3 RETURN_VALUE

このようにして、関数は として呼び出されf()ます。欠落している引数があることを検出し、それぞれのパラメーターにデフォルト値を入力します。これはすべて、関数が呼び出される前に発生します。関数内から、値が取得されて返されることがわかります。

そして、あなたのタスクを達成するためのさらに別の方法があります: 値を取るかのようにラムダを使用するだけです: lambda i: i. これを呼び出すと、引数の欠落について文句を言います。

しかし、あなたはそれを使用して対処することができますfunctools.partial:

ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()

このラッパーは、callable と渡されるいくつかの引数を取得します。結果のオブジェクトは、これらの引数と指定した引数を使用して元の callable を呼び出す callable です。ここで使用して、意図した値に固定しておくことができます。

于 2013-07-16T08:21:24.027 に答える