Pythonでcallbackを使う時に引数を取り扱う方法について。
Pythonでcallback
Pythonでcallbackを使う方法として最も簡単な形としては以下の様な感じ。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| >>> class CallbackExecutor():
...
... def __init__(self, callback):
... self.callback = callback
...
... def execute(self):
... self.callback('execute')
...
... def echo(v):
... print 'echo: %s' % v
...
>>> a = CallbackExecutor(echo)
>>> a.execute()
echo: execute
|
echo
という関数を作ってそれをCallbackExecutor
というクラスの
オブジェクトに渡しています。
CallbackExecutor
の中ではこの関数を保持していてexecute
を呼ぶと
その中でコールバックを呼ぶようになっています。
callbackに引数を渡す
ここで、コールバックの関数に別の引数を渡して、
引数によって動作を変えたいことがあります。
1
2
| def echo(v, name='defaultName'):
print '%s: %s' % (name, v)
|
こんな関数を作ってその場その場でname
を変えたい時。
ただし、コールバックを渡す時に()
を付けて実行してしまうと
渡すものが関数を実行した結果
になってしまうので上手く行きません。
1
2
3
4
5
6
7
| >>> a = CallbackExecutor(echo('any', 'myName'))
myName: any
>>> a.execute()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in execute
TypeError: 'NoneType' object is not callable
|
元のクラスの方で別の変数を取り入れる様にしてあげる方法もありますが、
直接関数に渡す方法として、
lambda式を使う方法があります。
1
2
3
| >>> a = CallbackExecutor(lambda x: echo(x, 'myName'))
>>> a.execute()
myName: execute
|
callbackへ渡す引数に変数を使う時の注意
ただし、ここでmyName
の所に変数を使う時には注意が必要です。
1
2
3
4
5
6
7
8
9
10
11
| >>> a = {}
>>> for name in ['myName', 'yourName']:
... a[name] = CallbackExecutor(lambda x: echo(x, name))
... a[name].execute()
...
myName: execute
yourName: execute
>>> a['myName'].execute()
yourName: execute
>>> a['yourName'].execute()
yourName: execute
|
と、最初にCallbackExecutorのオブジェクトを作った直後には
きちんとmyName
が使われていますが、
一度ループを終わった後にもう一度使ってみると
両方共yourName
になっています。
これは、渡されてるのはlambdaで定義された式なので、
この部分ではname
は解決されていない状態です。
従って、execute()
が実行された時点で初めてname
の値を見て結果を出しています。
実際に上の続きでname
を削除すると
1
2
3
4
5
6
7
8
| >>> del name
>>> a['myName'].execute()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in execute
File "<stdin>", line 2, in <lambda>
NameError: global name 'name' is not defined
>>>
|
の様にname
が見つけられなくてエラーを出します。
この様に関数に引数を渡したい場合には、
lambda式の変数にデフォルト引数として値を入れてあげると実現できます。
1
2
3
4
5
6
7
8
9
10
11
| >>> a = {}
>>> for name in ['myName', 'yourName']:
... a[name] = CallbackExecutor(lambda x, y=name: echo(x, y))
... a[name].execute()
...
myName: execute
yourName: execute
>>> a['myName'].execute()
myName: execute
>>> a['yourName'].execute()
yourName: execute
|
こんな感じ。
ここではy
のデフォルト値は式を作った時点でのname
になるので、
ループが終わってname
の値が変更されても
myName
の物はmyName
として残っています。