На днях у меня в форуме возникли почти подряд два топика про сигналы. Оба напомнили мне давно закравшееся в голове подозрение о том, что для многих сигналы — это магия из серии "если что-то не выходит, наверняка для этого нужны сигналы" :-).

Хочу раскрыть тему, потому как рамки форумного ответа особо разгуляться не позволяют.

Сигналы синхронны

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

Но сигналы тут совсем ни причем. Не знаю, откуда берется идея, что они выполняются в каком-то другом времени. Возможно из-за того, что механизм их работы где-то там в глубине скрыт, и просто хочется верить, что сделано так как раз для асинхронности (а для чего ещё?). А может виной всему опыт использования POSIX'овых сигналов, которые присылаются ОС в некие произвольные моменты времени и с точки зрения программы действительно происходят асинхронно с её процессом исполнения.

На самом же деле механизм вызова сигнала по сути своей прост, как доска. Точнее, как цикл "for". Представьте, что у вас где-то в системе есть глобальный словарик такого рода:

signals = {
    signal1: [func1, func2],
    signal2: [func3],
}

И вызов например signal1.send(*args) — это:

for func in signals[signal1]:
    func(*args)

Вот и всё. И пока весь цикл не пройдёт и все обработчики не отработают, вызов "send" будет честно их ждать.

Слабая связанность компонентов

Разобравшись с тем, чего сигналы не делают, осталось понять, зачем нужен такой странный способ вызывать функции.

Представим для примера некий гипотетический Форум, в котором есть авторизация по OpenID. Когда в форум приходит новый пользователь, код авторизации заводит новую запись форумного Профиля для этого пользователя. Делает он это самым простым и очевидным способом: импортирует модель Profile и создает её новый объект.

Теперь представим, что нам хочется оторвать авторизацию по OpenID в отдельное приложение — некий гипотетический Правильный OpenID-консумер. Просто перенести весь авторизационный код в отдельную директорию недостаточно из-за сильной связности: Авторизация всё ещё импортирует к себе модель Форума Profile, а значит её нельзя будет использовать как отдельное приложение без Форума.

Это как раз и решается сигналами. Вместо того, чтобы звать Форум напрямую, Авторизация в момент осознания, что с ней случился новый пользователь, отсылает в Бесконечный Эфир сигнал. Это специальный сигнал, описанный специально для такой оказии где-то в коде Авторизации и основательно документирован. Со своей стороны Форум импортирует определение сигнала из Авторизации и начинает его слушать, повесив на него в качестве реакции свою функцию, создающую новому пользователю профиль.

Таким образом мы получаем связь с однонаправленной зависимостью:

Вот эта слабая связность и есть то, для чего в первую очередь существуют сигналы. Она позволяет слабо связному компоненту разрабатываться отдельно и гарантированно не ломать систему до тех пор, пока соблюдается документированный протокол сигнала.

P.S. Создание системы со слабой связностью в обоих направлениях остаётся в качестве упражнения читателю.

Комментарии: 16 (feed)

  1. glader.livejournal.com

    С интересом прочитаю пример системы со слабой связностью в обоих направлениях. Мне видится затык в одном месте. Чтобы форуму начать ловить сигнал от авторизации, этот сигнал надо импортировать. Импорт - это сильная связность. Как избавиться от импорта - не понимаю. Читатели, help!

  2. wiz

    Цепляться на сигнал post_save у самого юзера, а не у профиля. Но это только переносит связку уровнем выше.

  3. krig

    То есть сигналы - это просто реализация паттерна Observer.

  4. Ivan Sagalaev

    То есть сигналы - это просто реализация паттерна Observer.

    Ага, точно.

  5. Andrey

    То есть сигналы - это просто реализация паттерна Observer.

    Только (как к сожалению бывает в Питоне) крайне неудачно выбрано имя. Listener в данном контексте куда лучше и точнее.

  6. Ivan Sagalaev

    На самом деле, сам модуль называется dispatcher (pydispatcher в доджанговской инкарнации), и это правомерный синоним паттерна наряду с Observer и Listener. Но вот народ как-то больше полюбил называть это словом сигналы.

  7. Александр Кошелев

    Чтобы форуму начать ловить сигнал от авторизации, этот сигнал надо импортировать.

    А зачем его ловить? Форум также может дать некий абстрактный интерфейс взаимодействия со сторонними приложениями авторизации и не быть завязанным на какую-то конкретно.

    Конечно связывание этого приложения авторизации и этого приложения форума может происходить как на уровне проекта, так и в каком-то третьем приложении.

  8. pyobject.ru/blog

    Бесконечный Эфир порадовал, но если бы был Мировой Эфир, вообще бы здорово было ;-)

  9. Сергей Кищенко

    Только (как к сожалению бывает в Питоне) крайне неудачно выбрано имя. Listener в данном контексте куда лучше и точнее.

    Названий у этого паттерна дофига. Publisher-Subscriber, Signall-Slot, просто Signals, Observer - всё это практически одно и то же. А асинхронные - это Deferred :)

    @isagalaev
    Странно, что это всё нужно рассказывать питонистам - мне казалось, что в питон уходят люди, обычно имеющие уже некоторую теоретическую базу.

  10. Ivan Sagalaev

    Сергей, тут есть три соображения.

    Во-первых, времена меняются, и многие приходят в Питон без какой бы то ни было базы. Отчасти из-за роста популярности языка, отчасти их привлекают развитые прикладные средства, вроде Джанги. По многим вопросам в джанговских форумах и maillist'ах видно, что люди пришли делать сайты на Джанге, а Питон и программирование, как таковое, изучают уже по ходу.

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

    Ну а в-третьих, я посты часто пишу порывом. Сложилось какие-то мысли в голове, вот и написал. :-)

  11. ei-grad

    А зачем его ловить? Форум также может дать некий абстрактный интерфейс взаимодействия со сторонними приложениями авторизации и не быть завязанным на какую-то конкретно.

    Такой велосипед уже придуман и называется "Обработчик сигналов"...)

    Иван, не могли бы вы привести практический пример задачи с системой со слабой связностью в обоих направлениях?..

  12. Александр Кошелев

    Такой велосипед уже придуман и называется "Обработчик сигналов"...)

    Ага, это частный случай.

  13. [...] 26.06.2009 14:46 Сигналы для этого не предназначены. Читайте: http://softwaremaniacs.org/blog/2009/05/31/what-signals-for/ [...]

  14. Ivan Fedorov

    Иван, не могли бы вы привести практический пример задачи с системой со слабой связностью в обоих направлениях?..

    Ну лично мне кажется, что это надо, когда вы хотите написать два по сути независимых приложения, но которые могут работать вместе.

    В принципе простейший выход в данной ситуации - простейший промежуточный модуль в котором определены только сигналы. Модуль сам зависит только от Django, а оба приложения зависят от модуля.

    Я сейчас делаю такое решения для системы приёма платежей: сколько и каких будет подключено платёжных систем я не знаю, но факт ввода денег я должен всегда видеть в биллинге. Естественно биллинг ни в коем случае не должен зависеть от платёжных систем, а они от биллинга.

  15. Nick Lutsiuk

    По поводу паттернов и их названий вспоминается презентация Joe Gregorio под названием (The Lack of) Design Patterns in Python (в сети есть и пдф, и видео). В частности:

    If your language of choice, in this case Python,
    supports an idiom natively, you don't need a
    name for it.
    Nobody talks about the 'structured programming
    pattern', or the 'function pattern', or the 'object-
    oriented pattern'.

  16. neoascetic

    Мне кажется, немалая причина того, что многие думают об асинхронности сигналов в том, что разработчики думают о них с событиями в JavaScript.

Добавить комментарий

Format with markdown