Питон ссылку на callback в словарь
у меня есть класс, который задает набор функций обратного вызова (показано здесь как cb1
и cb2
). У меня есть карта, которую я хочу назвать после какого-то события.
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
вы можете определить проблему?
что происходит, когда класс инициализируется,значения of cb1
и cb2
(оба None
) копируются на карту. Поэтому даже после того, как пользователь "регистрирует" обратный вызов (назначая cb1
), значение на карте еще None
и ничего не вызывается.
поскольку в Python нет такой вещи, как "по ссылке", как это исправить?
5 ответов
почему бы не сделать ваш класс явно занимающиеся регистрацией?
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')
добавить функцию регистрации. В классе Foo:
def register(self, name, cb): self.map[name] = cb
и вместо:
f.cb1 = mycallback
использование:
f.register('one', mycallback)
с дескриптором делегата и немного обмана атрибутов.
class Delegate(object):
def __get__(self, instance, owner):
return instance._cbs.get(self, lambda x: None)
def __set__(self, instance, value):
if not hasattr(instance, '_cbs'):
instance._cbs = {}
instance._cbs[self] = value
def __delete__(self, instance):
if not hasattr(instance, '_cbs'):
instance._cbs = {}
instance._cbs[self] = lambda x: None
def __hash__(self):
return id(self)
class C(object):
cb1 = Delegate()
map = {'one': 'cb1'}
def test(self, cb):
getattr(self, self.map[cb])()
def foo():
print 'bar!'
c = C()
c.cb1 = foo
c.test('one')
почему вам нужно установить другую переменную для настройки обратного вызова, чем та, которая фактически используется для его выполнения? Если вы используете ту же переменную, проблема исчезнет.
С некоторым синтаксическим сахаром это может выглядеть так:
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
наоборот, все "по ссылке" в Python. Но вы копируете ссылку на None
в ваш словарь, и изменение исходного слота ничего не делает с этой ссылкой. Если вы хотите сохранить дополнительный уровень косвенности, самым простым способом было бы сохранить строки. Если все ваши обратные вызовы являются атрибутами этого класса, избавьтесь от map
и просто хранить список имен атрибутов обратного вызова. callback_names = ['cb1', 'cb2']
, а затем использовать getattr(self, callback_name)()
для вызова обратного вызова. Если нужно ... есть карта, то вы можете сделать map = {'one': 'cb1', 'two': 'cb2'}
.
вы также можете сделать что-то необычное со свойствами, но это кажется излишне сложным.