LoginSignup
0
1

More than 1 year has passed since last update.

外側の一時的なローカル変数を参照する際の注意点

Posted at

PyQtでウィジットを作成するときなど、関数やメソッドをコールバックとして利用するために気軽にlambdaを作成してパラメータを減らしたものをコールバックとしてセットするというようなことをすることがあります。
この場合、次の例のように、コールバックを生成するメソッド内の一時的なローカル変数を
コールバックの中で参照すると、その変数は自由変数となり、意図しない結果となる場合があるので注意が必要です。

class Parent:

    def __init__(self):
        self.children = {"foo": Child(), "bar": Child()}

        for name, child in self.children.items():
            child.get_name = lambda: self.get_child_name(name)

    def get_child_name(self, name):
        return name


class Child:
    pass

p.children["foo"].get_name()は、"foo"を返すことを期待しますが、実際は"bar"を返します。

>>> p = Parent()

>>> p.children["foo"].get_name()
'bar'

__init__の中で、nameはforループで使用する一時的なローカル変数として使っていますが、2つのChildオブジェクトのget_name属性に代入されるlambdaの中で使用されているため、自由変数になります。
forループの中で最初のChildオブジェクトにlambdaをセットするときはnameには"foo"がセットされていますが、その後もそのlambdaは、同じname変数を参照し続けるため、nameにはその後"bar"がセットされて意図しない動作となるわけです。下記のとおり、p.children["foo"].get_name()p.children["bar"].get_name()が同じ変数を参照していることが分かります。

>>> p.children["foo"].get_name() is p.children["bar"].get_name()
True

関連記事
* 自由変数の名前やデータを、関数オブジェクトの中で調べる
* ネストした関数定義のユースケース

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1