在 python 中,有没有一种方法可以检查函数是否是“生成器函数”?在调用之前?

发布于 2024-08-14 22:31:47 字数 643 浏览 8 评论 0原文

假设我有两个函数:

def foo():
  return 'foo'

def bar():
  yield 'bar'

第一个是普通函数,第二个是生成器函数。现在我想写这样的内容:

def run(func):
  if is_generator_function(func):
     gen = func()
     gen.next()
     #... run the generator ...
  else:
     func()

is_generator_function() 的简单实现会是什么样子?使用 types 包我可以测试 gen 是否是生成器,但我希望在调用 func() 之前这样做。

现在考虑以下情况:

def goo():
  if False:
     yield
  else:
     return

调用 goo() 将返回一个生成器。我认为 python 解析器知道 goo() 函数有一个yield 语句,我想知道是否可以轻松获取该信息。

谢谢!

Lets say I have two functions:

def foo():
  return 'foo'

def bar():
  yield 'bar'

The first one is a normal function, and the second is a generator function. Now I want to write something like this:

def run(func):
  if is_generator_function(func):
     gen = func()
     gen.next()
     #... run the generator ...
  else:
     func()

What will a straightforward implementation of is_generator_function() look like? Using the types package I can test if gen is a generator, but I wish to do so before invoking func().

Now consider the following case:

def goo():
  if False:
     yield
  else:
     return

An invocation of goo() will return a generator. I presume that the python parser knows that the goo() function has a yield statement, and I wonder if it possible to get that information easily.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

流星番茄 2024-08-21 22:31:47
>>> import inspect
>>> 
>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> print inspect.isgeneratorfunction(foo)
False
>>> print inspect.isgeneratorfunction(bar)
True
  • Python 2.6 版本中的新增功能
>>> import inspect
>>> 
>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> print inspect.isgeneratorfunction(foo)
False
>>> print inspect.isgeneratorfunction(bar)
True
  • New in Python version 2.6
滥情稳全场 2024-08-21 22:31:47
>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> import dis
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 ('foo')
              3 RETURN_VALUE        
>>> dis.dis(bar)
  2           0 LOAD_CONST               1 ('bar')
              3 YIELD_VALUE         
              4 POP_TOP             
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE        
>>> 

如您所见,主要区别在于 bar 的字节码将至少包含一个 YIELD_VALUE 操作码。我建议使用 dis 模块(当然,将其输出重定向到 StringIO 实例并检查其 getvalue),因为这为您提供了对字节码更改的稳健性的衡量标准 -操作码的确切数值将会改变,但反汇编的符号值将保持相当稳定;-)。

>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> import dis
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 ('foo')
              3 RETURN_VALUE        
>>> dis.dis(bar)
  2           0 LOAD_CONST               1 ('bar')
              3 YIELD_VALUE         
              4 POP_TOP             
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE        
>>> 

As you see, the key difference is that the bytecode for bar will contain at least one YIELD_VALUE opcode. I recommend using the dis module (redirecting its output to a StringIO instance and checking its getvalue, of course) because this provides you a measure of robustness over bytecode changes -- the exact numeric values of the opcodes will change, but the disassembled symbolic value will stay pretty stable;-).

拥有 2024-08-21 22:31:47

我已经实现了一个装饰器,它挂钩装饰函数返回/产生的值。它的基本原理是:

import types
def output(notifier):
    def decorator(f):
        def wrapped(*args, **kwargs):
            r = f(*args, **kwargs)
            if type(r) is types.GeneratorType:
                for item in r:
                    # do something
                    yield item
            else:
                # do something
                return r
    return decorator

它之所以有效,是因为装饰器函数被无条件调用:它是被测试的返回值。


编辑:根据 Robert Lujo 的评论,我最终得到了类似的结果:

def middleman(f):
    def return_result(r):
        return r
    def yield_result(r):
        for i in r:
            yield i
    def decorator(*a, **kwa):
        if inspect.isgeneratorfunction(f):
            return yield_result(f(*a, **kwa))
        else:
            return return_result(f(*a, **kwa))
    return decorator

I've implemented a decorator that hooks on the decorated function returned/yielded value. Its basic goes:

import types
def output(notifier):
    def decorator(f):
        def wrapped(*args, **kwargs):
            r = f(*args, **kwargs)
            if type(r) is types.GeneratorType:
                for item in r:
                    # do something
                    yield item
            else:
                # do something
                return r
    return decorator

It works because the decorator function is unconditionnaly called: it is the return value that is tested.


EDIT: Following the comment by Robert Lujo, I ended up with something like:

def middleman(f):
    def return_result(r):
        return r
    def yield_result(r):
        for i in r:
            yield i
    def decorator(*a, **kwa):
        if inspect.isgeneratorfunction(f):
            return yield_result(f(*a, **kwa))
        else:
            return return_result(f(*a, **kwa))
    return decorator
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文