2011-10-12 14 views
5

コールバック関数のセットを指定するクラスがあります(ここにはcb1cb2と表示されています)。私はいくつかのイベントの後に電話したいこれらのマップを保持しています。辞書のコールバックへのPythonリファレンス

class Foo: 
    cb1 = None 
    cb2 = None 

    def test(self, input): 
     for (name, callback) in map: 
      if name == input: 
       if callback: callback() 
       ... 

    map = {'one':cb1, 'two':cb2} 

def mycallback(): 
    print "mycallback()" 

f = Foo() 
f.cb1 = mycallback # Register our callback 
f.test('one')  # Nothing happens 

問題を見つけることができますか?

何が起こるかは、クラスが初期化されるとき、(両方Noneある)cb1cb2をマップにコピーされることです。したがって、ユーザーがコールバックを(cb1に割り当てることによって)コールした後でさえ、マップの値はまだNoneであり、何も呼び出されません。

Pythonで「参照」というようなことはないので、これをどのように修正すればよいですか?

+0

Nitpick:すべてはPythonで「参照によって」渡されます。 * name *ではなく、参照によるものです。名前を別のオブジェクトに再バインドすると、名前が指し示していたものへの他の参照は更新されません。 –

答えて

9

あなたのクラスを明示的に登録しないようにしましょう。

import collections 

class Foo(object): 
    handlers = None 

    def __init__(self): 
     self.handlers = collections.defaultdict(set) 

    def register(self, event, callback): 
     self.handlers[event].add(callback) 

    def fire(self, event, **kwargs): 
     for handler in self.handlers.get(event, []): 
      handler(**kwargs) 

foo = Foo() 
foo.register('one', mycallback) 
foo.fire('one') 
+0

あなたはそうです。私の辞書は実際に私が提示したよりも複雑です - それは構文解析のための関数への参照、および他のものを持っていますので、私は最初これを互換性のない解決策として見落としました。しかし、それを見て、これを行うには、明らかに最善の方法です。ありがとうございました! –

1

登録機能を追加します。

def register(self, name, cb): self.map[name] = cb 

、代わりの:

f.cb1 = mycallback 

使用:Fooのクラスでは逆に

f.register('one', mycallback) 
+0

これはありがとう - 他の人はちょうどそれにあなたを打ち負かしていた。ところで、私のOPにはタイプミスがありました。私は 'f.cb1 = mycallback'ではなく' cb1 = mycallback'を持っていましたので、回答を編集して反映させたいかもしれません。 –

-1

、すべてがPythonで "参照によって" です。しかし、Noneへの参照を辞書にコピーしていて、元のスロットを変更してもその参照には何もしません。余計なレベルの間接参照を保持したい場合は、最も簡単な方法は文字列を格納することです。すべてのコールバックがこのクラスの属性である場合は、mapを取り除き、コールバック属性名のリストを格納します。 callback_names = ['cb1', 'cb2']を呼び出し、getattr(self, callback_name)()を使用してコールバックを呼び出します。マップが必要な場合は、map = {'one': 'cb1', 'two': 'cb2'}を実行できます。

あなたはプロパティで何か面白いこともできますが、それは不必要に複雑に思えます。

0

デリゲートディスクリプタとトリッキーな属性があります。

+0

賢明ですが、私は質問者が実際にこの複雑な解決策を必要としないと考えています。 –

0

実際にコールバックを実行するために使用するものとは異なるコールバックをカスタマイズするために、別の変数を設定する必要があるのはなぜですか?同じ変数を使用すると、問題は消滅します。それはこのようになりますいくつかのシンタックスシュガーで

class CallbackMap(object): 
    pass 

class Foo(object): 
    callbacks = CallbackMap() 

    def test(self, input): 
     callback = getattr(Foo.callbacks, input) 
     if callback: callback() 

# setup defaults 
Foo.callbacks.one = None 
Foo.callbacks.two = some_default_callback 

# customize 
def mycallback(): 
    print "mycallback()" 

f = Foo() 
Foo.callbacks.one = mycallback # Register our callback 
f.test('one') # works