Почему неправильно использовать Deferred.result для получения результата Twisted Deferred объекта? - PullRequest
0 голосов
/ 11 июля 2019

Из того, что я прочитал, никогда не следует использовать атрибут result объекта Twisted Deferred для доступа к значению Deferred. Я подозреваю, что причина в том, что либо

  1. Никакой результат может быть недоступен во время доступа (поднимает AttributeError)
  2. Результат может быть не окончательным во время доступа (т.е. не все обратные вызовы запущены)

Есть ли когда-нибудь ситуация, когда уместно получить доступ к значению результата Отложенного? Есть ли лучший способ получить доступ к результату, чтобы присвоить его переменной или использовать его позже, не добавляя дополнительные обратные вызовы для Deferred?

Ответы [ 2 ]

1 голос
/ 15 июля 2019

Ваши подозрения в основном верны.

Никакой результат может быть недоступен во время доступа (поднимает 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.

0 голосов
/ 12 июля 2019

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

@inlineCallBacks def thingummy(): thing = yield makeSomeRequestResultingInDeferred() print(thing) # the result! hoorj!

https://twistedmatrix.com/documents/13.2.0/api/twisted.internet.defer.inlineCallbacks.html

...