Nice programing

파이썬에서 함수를 호출하기 전에 "생성기 함수"인지 확인하는 방법이 있습니까?

nicepro 2021. 1. 7. 21:19
반응형

파이썬에서 함수를 호출하기 전에 "생성기 함수"인지 확인하는 방법이 있습니까?


두 가지 기능이 있다고 가정 해 봅시다.

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()하면 생성기가 반환됩니다. 파이썬 파서가 goo()함수에 yield 문이 있다는 것을 알고 있다고 가정하고 그 정보를 쉽게 얻을 수 있는지 궁금합니다.

감사!


>>> import inspect
>>> 
>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> print inspect.isgeneratorfunction(foo)
False
>>> print inspect.isgeneratorfunction(bar)
True
  • Python 버전 2.6의 새로운 기능

사실, 그런 가설 is_generator_function()이 실제로 얼마나 유용한 지 궁금합니다 . 중히 여기다:

def foo():
    return 'foo'
def bar():
    yield 'bar'
def baz():
    return bar()
def quux(b):
    if b:
        return foo()
    else:
        return bar()

What should is_generator_function() return for baz and quux? baz() returns a generator but isn't one itself, and quux() might return a generator or might not.


>>> 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;-).


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

ReferenceURL : https://stackoverflow.com/questions/1871685/in-python-is-there-a-way-to-check-if-a-function-is-a-generator-function-before

반응형