[python] 파이썬 빈 생성기 함수

파이썬에서는 다음과 같이 yield 키워드를 함수 본문에 넣어 반복자 함수를 쉽게 정의 할 수 있습니다.

def gen():
    for i in range(100):
        yield i

값을 생성하지 않는 (0 값 생성) 생성기 함수를 어떻게 정의 할 수 있습니까? 다음 코드는 작동하지 않습니다. 왜냐하면 파이썬은 그것이 정상적인 함수가 아닌 생성기인지 알 수 없기 때문입니다.

def empty():
    pass

나는 다음과 같은 것을 할 수 있었다.

def empty():
    if False:
        yield None

그러나 그것은 매우 추한 것입니다. 빈 반복기 함수를 실현하는 좋은 방법이 있습니까?



답변

return생성기에서 한 번만 사용할 수 있습니다 . 아무것도 산출하지 않고 반복을 중지하므로 함수가 범위를 벗어나도록하는 것에 대한 명시적인 대안을 제공합니다. 따라서 yield함수를 제너레이터로 바꾸려면을 사용 하십시오. 그러나 return어떤 것을 산출하기 전에 제너레이터를 종료하려면 먼저 사용하십시오.

>>> def f():
...     return
...     yield
...
>>> list(f())
[]

나는 그것이 당신이 가진 것보다 훨씬 낫다는 것을 확신하지 못합니다-단지 no-op if문을 no-op 문으로 대체합니다 yield. 그러나 그것은 더 관용적입니다. 그냥 사용 yield하는 것은 작동하지 않습니다.

>>> def f():
...     yield
...
>>> list(f())
[None]

왜 그냥 사용하지 iter(())않습니까?

이 질문은 빈 생성기 함수 에 대해 구체적으로 묻습니다 . 따라서 일반적으로 빈 반복기를 만드는 가장 좋은 방법에 대한 질문 이라기보다는 Python 구문의 내부 일관성에 대한 질문으로 간주합니다.

질문이 실제로 빈 반복기를 만드는 가장 좋은 방법에 관한 것이라면iter(()) 대신 사용 하는 것에 대해 Zectbumo 에 동의 할 수 있습니다 . 그러나 iter(())함수를 반환하지 않는 것을 관찰하는 것이 중요 합니다! 빈 이터 러블을 직접 반환합니다. 이터 러블 을 반환 하는 콜 러블을 기대하는 API로 작업한다고 가정합니다 . 다음과 같이해야합니다.

def empty():
    return iter(())

( 이 답변의 첫 번째 올바른 버전을 제공 한 크레딧은 Unutbu 에 가야합니다 .)

이제 위의 내용이 더 명확해질 수 있지만 덜 명확 할 상황을 상상할 수 있습니다. (기여 된) 생성기 함수 정의의 긴 목록의 다음 예를 고려하십시오.

def zeros():
    while True:
        yield 0

def ones():
    while True:
        yield 1

...

긴 목록 끝에 다음과 같이 a가 yield있는 것을보고 싶습니다 .

def empty():
    return
    yield

또는 Python 3.3 이상에서 ( DSM 에서 제안한대로 ) 다음과 같습니다.

def empty():
    yield from ()

yield키워드 의 존재 는 이것이 다른 모든 함수와 똑같이 또 다른 제너레이터 함수라는 것을 아주 간략하게 보여줍니다. iter(())버전이 동일한 작업을 수행 하는지 확인하는 데 시간이 조금 더 걸립니다 .

미묘한 차이이지만 솔직히 yield기반 함수가 더 읽기 쉽고 유지 관리가 가능 하다고 생각합니다 .


답변

iter(())

발전기가 필요 하지 않습니다 . 어서!


답변

Python 3.3 (나는 yield from킥을했고 @senderle이 내 첫 생각을 훔 쳤기 때문에) :

>>> def f():
...     yield from ()
...
>>> list(f())
[]

그러나 나는 인정해야한다. 나는 이것에 대해 똑같이 잘 작동 iter([])하거나 (x)range(0)작동하지 않을 사용 사례를 찾는 데 어려움을 겪고있다 .


답변

또 다른 옵션은 다음과 같습니다.

(_ for _ in ())


답변

생성기 함수 여야합니까? 그렇지 않다면 어떨까요

def f():
    return iter([])


답변

빈 반복자를 만드는 “표준”방법은 iter ([])처럼 보입니다. 나는 []를 iter ();의 기본 인자로 만들 것을 제안했다. 이것은 좋은 인수 거부를 참조 http://bugs.python.org/issue25215을
– Jurjen을


답변

@senderle이 말했듯이 다음을 사용하십시오.

def empty():
    return
    yield

나는 이것에 대한 또 다른 정당성을 공유하기 위해 주로이 답변을 작성하고 있습니다.

이 솔루션을 다른 솔루션보다 선택하는 한 가지 이유는 인터프리터가 고려하는 한 최적이기 때문입니다.

>>> import dis
>>> def empty_yield_from():
...     yield from ()
...
>>> def empty_iter():
...     return iter(())
...
>>> def empty_return():
...     return
...     yield
...
>>> def noop():
...     pass
...
>>> dis.dis(empty_yield_from)
  2           0 LOAD_CONST               1 (())
              2 GET_YIELD_FROM_ITER
              4 LOAD_CONST               0 (None)
              6 YIELD_FROM
              8 POP_TOP
             10 LOAD_CONST               0 (None)
             12 RETURN_VALUE
>>> dis.dis(empty_iter)
  2           0 LOAD_GLOBAL              0 (iter)
              2 LOAD_CONST               1 (())
              4 CALL_FUNCTION            1
              6 RETURN_VALUE
>>> dis.dis(empty_return)
  2           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
>>> dis.dis(noop)
  2           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE

보시다시피, empty_return는 일반 빈 함수와 정확히 동일한 바이트 코드를 가지고 있습니다. 나머지는 어쨌든 동작을 변경하지 않는 여러 다른 작업을 수행합니다. 유일한 차이점 empty_returnnoop전자가 생성 플래그 세트를 갖는 것이다 :

>>> dis.show_code(noop)
Name:              noop
Filename:          <stdin>
Argument count:    0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals:  0
Stack size:        1
Flags:             OPTIMIZED, NEWLOCALS, NOFREE
Constants:
   0: None
>>> dis.show_code(empty_return)
Name:              empty_return
Filename:          <stdin>
Argument count:    0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals:  0
Stack size:        1
Flags:             OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE
Constants:
   0: None

물론이 주장의 강점은 사용중인 Python의 특정 구현에 따라 크게 달라집니다. 충분히 똑똑한 대체 통역사는 다른 작업이 유용하지 않다는 것을 알아 차리고 최적화 할 수 있습니다. 그러나 이러한 최적화가 존재하더라도 인터프리터는이를 수행하는 데 시간을 할애 iter하고 전역 범위 의 식별자가 다른 항목으로 리 바인드되는 것과 같이 최적화 가정이 깨지지 않도록 보호 해야합니다. 실제로 일어났습니다). 의 경우 empty_return가 너무도 상대적으로 순진한 CPython의 어떤 의사 작업에 시간을 낭비하지 않을 것이다, 단순히 최적화에 불과하다.