발전기 generator
와 편리한 방법이 있습니다 generate_all
.
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
할까요 generate_all
return
또는 yield
? 두 방법을 사용하는 사용자가 동일하게 사용하기를 원합니다. 즉
for x in generate_all()
와 같아야한다
some_list = get_the_list()
for x in generate(some_list)
답변
발전기는-게으른 평가 이렇게 있습니다 return
또는 yield
예외가 발생하는 경우 당신은 당신의 코드를 디버깅 할 때 다르게 행동이나됩니다.
와 return
당신에 발생하는 예외 generator
에 대해 아무것도 알 수 없습니다 generate_all
때 때문입니다, generator
정말 실행 이미 남아있는 generate_all
기능을. 함께 yield
거기가있을 것이다 generate_all
역 추적에.
def generator(some_list):
for i in some_list:
raise Exception('exception happened :-)')
yield i
def generate_all():
some_list = [1,2,3]
return generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
8 return generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-3-b19085eab3e1> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
그리고 그것을 사용한다면 yield from
:
def generate_all():
some_list = [1,2,3]
yield from generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
8 yield from generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-4-be322887df35> in generate_all()
6 def generate_all():
7 some_list = [1,2,3]
----> 8 yield from generator(some_list)
9
10 for item in generate_all():
<ipython-input-4-be322887df35> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
그러나 이는 성능 저하를 초래합니다. 추가 생성기 레이어에는 약간의 오버 헤드가 있습니다. 따라서 return
일반적으로 yield from ...
(또는 for item in ...: yield item
) 보다 약간 빠릅니다 . 대부분의 경우 생성기에서 수행하는 작업이 일반적으로 런타임을 지배하므로 추가 계층이 눈에 띄지 않기 때문에 이것은 중요하지 않습니다.
그러나 yield
몇 가지 추가 장점이 있습니다. 단일 iterable로 제한되지 않고 추가 항목을 쉽게 생성 할 수도 있습니다.
def generator(some_list):
for i in some_list:
yield i
def generate_all():
some_list = [1,2,3]
yield 'start'
yield from generator(some_list)
yield 'end'
for item in generate_all():
print(item)
start
1
2
3
end
귀하의 경우 작업이 매우 간단하고이를 위해 여러 함수를 작성해야하는지도 모르겠습니다 map
. 대신 내장 또는 생성기 표현식을 대신 사용할 수 있습니다 .
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
둘 다 동일해야합니다 (예외가 발생할 때 약간의 차이는 제외). 그리고 그들이 더 설명적인 이름이 필요하다면, 당신은 여전히 그것들을 하나의 기능으로 포장 할 수 있습니다.
iterables 빌트인에 대한 매우 일반적인 조작을 랩핑하는 여러 헬퍼가 있으며, 빌트인 itertools
모듈 에서 추가 툴을 찾을 수 있습니다 . 그런 간단한 경우에 나는 단순히 이것에 의지하고 사소한 경우에만 자신의 발전기를 씁니다.
그러나 실제 코드가 더 복잡하여 적용 할 수 없을 수도 있지만 대안을 언급하지 않으면 완전한 답변이 아니라고 생각했습니다.
답변
간단한 반복자의 경우
yield from iterable
본질적으로 단축 된 형태입니다.for item in iterable: yield item
def generator(iterable):
for i in iterable:
yield do_something(i)
def generate_all():
yield from generator(get_the_list())
꽤 간결하고 임의 / 다른 반복 가능 체인을 연결할 수있는 것과 같은 다른 많은 장점이 있습니다!
답변
return generator(list)
당신이 원하는 것을합니다. 그러나 참고
yield from generator(list)
동일하지만, generator
소진 된 후에 더 많은 가치를 산출 할 수있는 기회가 주어집니다 . 예를 들면 다음과 같습니다.
def generator_all_and_then_some():
list = get_the_list()
yield from generator(list)
yield "one last thing"
답변
이 특별한 경우에 다음 두 문장은 기능적으로 동등한 것으로 보입니다.
return generator(list)
과
yield from generator(list)
후자는 대략
for i in generator(list):
yield i
이 return
명령문은 찾고있는 생성기를 리턴합니다. yield from
또는 yield
문은 무언가로 그 반환 당신이 찾고있는 하나를 통과 발전기를, 전체 기능을집니다.
사용자 관점에서는 차이가 없습니다. 그러나 내부적 으로 불필요한 패스 스루 생성기에 싸이 지 return
않기 때문에 더 효율적 generator(list)
입니다. 랩핑 된 생성기의 요소에 대한 처리 를 계획중인 경우 , 어떤 형태 yield
의 과정을 사용하십시오.
답변
당신은 return
그것을 할 것입니다.
yield
ing *을 사용하면 generate_all()
생성기 자체를 평가할 수 있으며 next
해당 외부 생성기를 호출 하면 첫 번째 함수에서 반환 한 내부 생성기가 반환됩니다.
*
를 포함하지 않는 yield from