是否存在生成器装饰器?

2 投票
3 回答
2635 浏览
提问于 2025-04-17 01:44

我刚刚在使用Twisted的时候,追踪到一个随机出现的bug:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/spread/pb.py", line 826, in proto_message
    self._recvMessage(self.localObjectForID, requestID, objectID, message, answerRequired, netArgs, netKw)
  File "/usr/lib/python2.7/dist-packages/twisted/spread/pb.py", line 840, in _recvMessage
    netResult = object.remoteMessageReceived(self, message, netArgs, netKw)
  File "/usr/lib/python2.7/dist-packages/twisted/spread/flavors.py", line 114, in remoteMessageReceived
    state = method(*args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 1141, in unwindGenerator
    return _inlineCallbacks(None, f(*args, **kwargs), Deferred())
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 1020, in _inlineCallbacks
    result = g.send(result)
exceptions.AttributeError: 'NoneType' object has no attribute 'send'

这个问题是由以下原因引起的:

@defer.inlineCallbacks
def myfunc():
    # Function implementation with no yield statement.

当调用myfunc的时候,我会看到之前的错误信息被打印出来,但函数内部的所有东西都正常工作。这是因为它返回了None,而不是一个生成器,而defer.inlineCallbacks是期待返回一个生成器的。有没有办法在函数体内不放置yield语句的情况下,把一个函数声明为生成器?比如说使用一个生成器装饰器?

3 个回答

1

看起来你遇到了问题单 2501。这个问题已经在最新版本中修复了,以后你遇到的错误信息会更清晰明了。

1

如果你真的需要这个,那就自己想一个吧。

#like this
def generator(callable):
    def asgenerator(*args, **kwargs):
        while True:
            yield callable(*args, **kwargs)
    return asgenerator

不过,正如其他人提到的,这可能是在掩盖一个设计上的问题。

6

正如其他人提到的,这可能听起来有点奇怪。不过为了完整性和回答这个问题:

,你必须使用 yield 来让它变成一个生成器,当然,如果你创建一个本身就是生成器的装饰器(通过包含 yield 关键字),但实际上并不返回任何东西,只是调用被装饰的函数,那也是可以的。这样的 yield 可能是不可达的,也没有实际意义(比如 if False: yield),但它必须存在。因为这种需求并不常见,所以没有现成的解决方案,至少我不知道有什么。最简单的方法就是直接在你的函数中添加这个,自己写一个装饰器对于少数情况来说并不划算,如果你经常需要这样做,可能你的设计有问题,应该先去修正它。

撰写回答