파이썬에서는 다음과 같이 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_return
과 noop
전자가 생성 플래그 세트를 갖는 것이다 :
>>> 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의 어떤 의사 작업에 시간을 낭비하지 않을 것이다, 단순히 최적화에 불과하다.