Python: обратные вызовы, делегаты, ...? Что общего?

Просто хочу знать, как обычно реагируют на события в Python. В других языках есть несколько способов, таких как функции обратного вызова, делегаты, структуры слушателей и так далее. Есть общий способ? Какие существуют языковые концепции или дополнительные модули по умолчанию и какие вы можете порекомендовать?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
24
0
22 642
8

Ответы 8

Лично я видел только обратные вызовы. Однако я не видел такого количества кода Python, управляемого событиями, поэтому YMMV.

Я не могу говорить об общих подходах, но эта страница (фактическая копия недоступна) имеет реализацию шаблона наблюдателя, который мне нравится.

Вот ссылка на Интернет-архив: http://web.archive.org/web/20060612061259/http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html

Я видел, как используются слушатели и обратные вызовы. Но AFAIK нет способа Python. Они должны быть в равной степени осуществимы, если рассматриваемое приложение подходит.

Лично я не вижу разницы между обратными вызовами, слушателями и делегатами.

образец наблюдателя (он же слушатели, он же «множественные обратные вызовы») легко реализовать - просто храните список наблюдателей и добавляйте или удаляйте из него вызываемые объекты. Эти вызываемые объекты могут быть функциями, связанными методами или классами с магическим методом __call__. Все, что вам нужно сделать, это определить интерфейс, который вы ожидаете от них - например, получают ли они какие-либо параметры.

class Foo(object):
  def __init__(self):
    self._bar_observers = []

  def add_bar_observer(self, observer):
    self._bar_observers.append(observer)

  def notify_bar(self, param):
    for observer in self._bar_observers:
      observer(param)

def observer(param):
  print "observer(%s)" % param

class Baz(object):
  def observer(self, param):
    print "Baz.observer(%s)" % param

class CallableClass(object):
  def __call__(self, param):
    print "CallableClass.__call__(%s)" % param

baz = Baz()

foo = Foo()

foo.add_bar_observer(observer) # function
foo.add_bar_observer(baz.observer) # bound method
foo.add_bar_observer(CallableClass()) # callable instance

foo.notify_bar(3)

хороший пример, но как насчет упомянутого вами 'вызов', позволяет ли он регистрировать foo.add_bar_observer(callable)?

Kevin 28.04.2011 15:32

@Kevin - добавил пример с __call__. Вы можете узнать больше об этом (и других специальных именах методов) на docs.python.org/reference/datamodel.html

orip 05.04.2012 12:29

Думаю в вашем обновлении ошибка, SomeCallable() должен читать SomeCallable (сам не уверен, что исправлю сам, при необходимости удалю комментарий)

Kevin 05.04.2012 12:36

@Kevin - я считаю, что с обновлением все в порядке (и работает). Вызываются экземпляры SomeCallable. Я поменяю название класса :)

orip 05.04.2012 15:19

не могли бы вы объяснить, почему вы унаследовали от объекта? и как метод notify_bar знает, какую функцию / метод наблюдателя он вызывает?

Lucky 28.08.2017 19:40

@Lucky, унаследованный от object, - это древняя причуда python2, гарантирующая, что мы получим классы нового стиля вместо классов старого стиля. Необязательно в python3. notify_bar не знает и не заботится о том, какой вызываемый объект вызывает. Раньше он добавлял их в список, а теперь вызывает их. Насколько это возможно, они непрозрачны.

orip 28.08.2017 20:50

Все зависит от уровня сложности вашего приложения. Для простых событий, вероятно, подойдут обратные вызовы. Для более сложных шаблонов и разделенных уровней вы должны использовать какую-то реализацию публикации-подписки, такую ​​как PyDispatcher или pubsub wxPython.

См. Также это обсуждение.

Большинство библиотек Python, которые я использовал, реализуют модель обратного вызова для своих уведомлений о событиях, что, на мой взгляд, достаточно хорошо подходит для языка. Pygtk делает это, производя все объекты из GObject, который реализует обработку сигналов на основе обратного вызова. (Хотя это особенность базовой реализации C GTK, а не то, что было вдохновлено языком.) Однако Pygtkmvc выполняет интересную работу по реализации шаблона наблюдателя (и MVC) поверх Pygtk. Он использует очень богатую реализацию на основе метаклассов, но я обнаружил, что в большинстве случаев она работает достаточно хорошо. Код достаточно прост для выполнения, если вам интересно увидеть один из способов, которым это было сделано.

Модуль matplotlib.cbook содержит класс CallbackRegistry, на который вы, возможно, захотите взглянуть. Из документация:

Handle registering and disconnecting for a set of signals and
callbacks:

   signals = 'eat', 'drink', 'be merry'

   def oneat(x):
       print 'eat', x

   def ondrink(x):
       print 'drink', x

   callbacks = CallbackRegistry(signals)

   ideat = callbacks.connect('eat', oneat)
   iddrink = callbacks.connect('drink', ondrink)

   #tmp = callbacks.connect('drunk', ondrink) # this will raise a ValueError

   callbacks.process('drink', 123)    # will call oneat
   callbacks.process('eat', 456)      # will call ondrink
   callbacks.process('be merry', 456) # nothing will be called
   callbacks.disconnect(ideat)        # disconnect oneat
   callbacks.process('eat', 456)      # nothing will be called

Вероятно, вам не нужна зависимость от пакета matplotlib. Я предлагаю вам просто скопировать и вставить класс в свой собственный модуль из исходный код.

Я ищу реализацию для регистрации и обработки событий в Python. Мой единственный опыт связан с Gobject, но я использовал его только с PyGtk. Он гибкий, но может быть слишком сложным для некоторых пользователей. Я также встретил несколько других интересных кандидатов, но непонятно, как именно они сравниваются друг с другом.

Другие вопросы по теме