为什么使用 Deferred.result 获取 Twisted Deferred 对象的结果是错误的?

Why is it wrong to use Deferred.result to get the result of a Twisted Deferred object?

根据我的阅读,永远不要使用 Twisted Deferred 对象的 result 属性来访问 Deferred 的值。我怀疑这样做的原因是

  1. 访问时可能没有结果(加薪 AttributeError)
  2. 访问时结果可能不是最终的(即并非所有 回调有 运行)

是否存在适合访问 Deferred 结果值的情况?有没有更好的方法来访问结果以将其分配给变量或稍后使用它而不向 Deferred 添加额外的回调?

我想这就是您要找的。使用内联回调,您无需编写回调即可访问结果。

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

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

你的怀疑基本上是正确的。

No result may not be available at the time of access (raises AttributeError)

Deferred 的想法是一些工作正在进行,并且不会在某个固定时间完成。当它完成时它就会完成。幸运的是,当它完成时,Deferred 可以告诉你结果——这就是回调的目的。这意味着,如果不使用 回调,您无法知道工作何时完成。您可能过早检查并得到 AttributeError (或另一种失败,更多内容见下文)。您可能检查得太晚而浪费了一些时间。您可以通过反复检查和处理错误来 "fix" too-early 案例。这称为 "polling"。 Twisted 的大部分存在是为了消除执行轮询的需要,因为轮询通常是不可取的(它很昂贵)。 "fix" too-late 情况的唯一方法是更频繁地检查 - 这会使 "too-early" 情况变得更糟。

The result may not be final at the time of access (i.e. not all callbacks have run)

Deferred 提供的一个功能是它允许在 event-driven 编程中进行 组合 。您可以让执行某些任务的单元 A 和 returns 一个 Deferred。您可以让单元 B 执行某些任务并依赖于单元 A 和 returns 其他一些 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 被回调的任何值。在这种情况下,您没有公开不完整或中间的结果,也没有问题。但是,这种组合很笨拙、冗长,而且 failure-prone(例如,它缺少错误传播和取消功能)。

编写 Deferred API 的简单、惯用、值得鼓励的方法是:

d = unit_a()
d.addCallback(unit_b)

更易于编写,更易于阅读,并且您可以获得 railroad-style 错误传播等功能。

在这种情况下,d.result 的值是多少?首先是 AttributeError。那么,unit_a完成后,d.result就是unit_b返回的Deferred。所以如果你在这里查找,你不仅没有最终结果而且你甚至没有真正的价值,你有一个 Deferred 代替。只有在 unit_b 完成后,d 才会呈现 unit_b.

的真实结果

还有其他可能的序列。它们来自其他 Deferred 使用模式,这些模式不像上面的那样受欢迎,但它们肯定是可能的。例如,您可能有一个实现:

d = unit_a()

然后让 d 暴露给您的代码。在那之后,您可能会让实施继续进行:

d.addCallback(unit_b)

现在 d.resultAttributeErrorunit_a 的结果再到 Deferred 再到 unit_b 的结果。以上并不是 Deferred 的完美用法,但如您所见,这是完全可能的。如果您在这种情况下轮询 d.result,您将不得不猜测非 Deferred 值是 unit_a 还是 unit_b 的结果。

Is there ever a situation where it is appropriate to access the value of the result of the Deferred?

很难完全排除。当然,在 Deferred 本身的测试套件中有一些直接访问,这些可能是合法的。在其他一些 test-related 工具中,以这种方式访问​​ result 属性可能是合适的——但理想情况下,即使在那里也应该避免这种情况,或者测试库(例如 Twisted 的试用版)会提供更好的工具编写这些类型的测试(例如,trial 确实提供 successResultOffailureResultOfassertNoResult)。除了这些情况,我认为您应该非常、非常仔细地查看 result 属性的任何使用,您可能会发现使用回调有更好(更易于维护、更不脆弱)的解决方案。

Is there a better way to access the result to assign it to a variable or use it later without adding additional callbacks to the Deferred?

访问结果的更好方法始终是添加一个额外的回调。正如另一个答案所暗示的那样,inlineCallbacks 是一种使用回调的方法,它 看起来 不像使用回调,并且受到许多人的青睐。它允许您对本地进行简单赋值以检索结果,但实现仍在使用 Deferred.addCallback 和相关 API。