Ваши подозрения в основном верны.
Никакой результат может быть недоступен во время доступа (поднимает AttributeError)
Идея Deferred
заключается в том, чтокакая-то работа происходит, и она не будет закончена в какое-то определенное время.Это будет закончено, когда это будет закончено.К счастью, когда он закончен, Deferred
может рассказать вам о результате - для этого нужны обратные вызовы.Это означает, что без с использованием обратных вызовов вы не сможете узнать, когда работа будет завершена.Вы можете проверить слишком рано и получить AttributeError
(или ошибку другого рода, подробнее об этом ниже).Вы можете проверить слишком поздно и потеряли время.Вы можете «исправить» слишком ранний случай, повторно проверяя и обрабатывая ошибку.Это называется "опрос".Существуют большие части Twisted для устранения необходимости выполнения опроса, потому что опрос обычно нежелателен (это дорого).Единственный способ «исправить» слишком поздний случай - это проверить чаще, что еще хуже делает «слишком ранний» случай.
Результат может быть не окончательным во время доступа (т.е. не все обратные вызовы были запущены)
Функция, предлагаемая Deferred
, заключается в том, что она позволяет композиция в программировании, управляемом событиями.У вас может быть блок A, который выполняет некоторую задачу и возвращает Deferred
.У вас может быть блок B, который выполняет какую-то задачу и зависит от блока A и возвращает какой-то другой Deferred
.Такая композиция может выглядеть следующим образом:
d = Deferred()
d_a = unit_a()
def a_finished(result):
d_b = unit_b(result)
d_b.addCallback(d_final.callback)
d_a.addCallback(a_finished)
Какие значения принимает d.result
в ходе выполнения этого примера?Сначала это AttributeError
, а затем это значение, с которым unit_b
Deferred
вызывается обратно.В этом случае у вас нет неполных или промежуточных результатов, и нет никаких проблем.Однако эта композиция неудобна, многословна и подвержена ошибкам (например, она пропускает функции распространения и отмены ошибок).
Простой, идиоматичный, поощряемый способ составления Deferred
API:
d = unit_a()
d.addCallback(unit_b)
Легче писать, легче читать, и вы получаете такие функции, как распространение ошибок в стиле железной дороги.
Какие значения принимает d.result
в этом случае?Сначала это AttributeError
.Затем, после выполнения unit_a
, d.result
- это Deferred
, возвращаемое unit_b
.Так что, если вы посмотрите здесь, у вас не только нет конечного результата, но вы даже не имеете реального значения, вместо этого у вас есть Deferred
.Только после выполнения unit_b
d
получает реальный результат unit_b
.
Также возможны и другие последовательности.Они возникают из других Deferred
шаблонов использования, которые не так предпочтительны, как приведенные выше, но, безусловно, возможны.Например, у вас может быть реализация, которая выполняет:
d = unit_a()
, а затем d
отображается в вашем коде.После этого момента реализация может перейти к:
d.addCallback(unit_b)
Теперь d.result
переходит от AttributeError
к результату unit_a
к Deferred
к результату unit_b
.Вышесказанное не является превосходным использованием Deferred
, но, как вы можете видеть, это вполне возможно.Если вы опрашиваете d.result
в этом случае, у вас есть сложность угадать, является ли значение, отличное от Deferred
, результатом unit_a
или unit_b
.
Isкогда-нибудь возникала ситуация, когда уместно получить доступ к значению результата Отложенного?
Трудно полностью исключить это.Конечно, в наборе тестов для самого Deferred
есть несколько прямых обращений, и они могут быть законными.Вполне возможно, что в некоторых других инструментах, связанных с тестированием, было бы целесообразно получить доступ к атрибуту result
таким образом, но в идеале даже этого можно было бы избежать, или библиотеки тестирования (такие как пробная версия Twisted) предоставили бы лучшие инструменты для написания таких типовтесты (например, пробные версии successResultOf
, failureResultOf
и assertNoResult
).Помимо этих случаев, я думаю, что вам следует очень внимательно следить за любым использованием атрибута result
, и вы, вероятно, обнаружите, что существует лучшее (более удобное в обслуживании, менее хрупкое) решение с использованием обратных вызовов.
Есть ли лучший способ получить доступ к результату, чтобы присвоить его переменной или использовать его позже, не добавляя дополнительные обратные вызовы для Deferred?
Лучший способ получить доступ к результату - это всегда добавить дополнительный обратный вызов. Как показывает другой ответ, inlineCallbacks
- это способ использования обратных вызовов, который не выглядит как использование обратных вызовов и является предпочтительным для многих. Это позволяет вам выполнить простое назначение для локального получения результата, но реализация все еще использует Deferred.addCallback
и соответствующие API.