python 의 yield 와 generator 를 쉽게 설명 합 니 다.
본 고 는 yield 와 generator 를 간단하게 상세 하 게 소개 할 것 이다.다음 과 같은 내용 을 포함한다.어떤 generator,generator 를 생 성 하 는 방법,generator 의 특징,generator 기초 와 고급 응용 장면,generator 사용 중의 주의사항 을 포함한다.본 고 는 enhanced generator 즉 pep 342 와 관련 된 내용 을 포함 하지 않 으 며 이 부분 은 나중에 소개 합 니 다.
발전기 기초
python 의 함수(function)정의 에서 yield 표현 식(Yield expression)이 나타 나 면 사실상 generator function 을 정의 합 니 다.이
generator function
반환 값 을 호출 하면 generator 입 니 다.이 일반적인 함수 호출 은 차이 가 있 습 니 다.For example:
def gen_generator():
yield 1
def gen_value():
return 1
if __name__ == '__main__':
ret = gen_generator()
print ret, type(ret) #<generator object gen_generator at 0x02645648> <type 'generator'>
ret = gen_value()
print ret, type(ret) # 1 <type 'int'>
위의 코드 를 통 해 알 수 있 듯 이gen_generator
함수 가 돌아 온 것 은 generator 인 스 턴 스 입 니 다.generator 는 다음 과 같은 특별한 점 이 있 습 니 다.
•교체 기(iterator)프로 토 콜 에 따라 교체 기 프로 토 콜 은 실현
__iter__
,next 인터페이스 가 필요 합 니 다.•여러 번 들 어 갈 수 있 고 여러 번 돌아 갈 수 있 으 며 함수 체 의 코드 실행 을 중단 할 수 있 습 니 다.
테스트 코드 를 살 펴 보 겠 습 니 다.
>>> def gen_example():
... print 'before any yield'
... yield 'first yield'
... print 'between yields'
... yield 'second yield'
... print 'no yield anymore'
...
>>> gen = gen_example()
>>> gen.next() # next
before any yield
'first yield'
>>> gen.next() # next
between yields
'second yield'
>>> gen.next() # next
no yield anymore
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteratio
gen example 방법 을 호출 하 는 것 은 아무런 내용 도 출력 하지 않 았 습 니 다.함수 체 의 코드 가 아직 실행 되 지 않 았 음 을 설명 합 니 다.generator 의 next 방법 을 호출 하면 generator 는 yield 표현 식 에 실 행 됩 니 다.yield 표현 식 의 내용 을 되 돌려 주 고 이 곳 에서 일시 정지(걸 기)하기 때문에 처음으로 next 를 호출 하여 첫 번 째 문장 을 인쇄 하고"first yield"를 되 돌려 줍 니 다.일시 정 지 는 방법의 국부 변수,포인터 정보,운행 환경 을 다음 호출 next 방법 이 복 구 될 때 까지 저장 합 니 다.두 번 째 next 호출 후 마지막 yield 에서 중단 하고 다시 호출next()
방법 을 사용 하면 StopIteration 이상 을 던 집 니 다. for 문 구 는 StopIteration 이상 을 자동 으로 캡 처 할 수 있 기 때문에 generator(본질 적 으로 모든 iterator)가 자주 사용 하 는 방법 은 순환 에서 사용 합 니 다.
def generator_example():
yield 1
yield 2
if __name__ == '__main__':
for e in generator_example():
print e
# output 1 2
generator function 에서 발생 하 는 generator 는 일반적인 function 과 어떤 차이 가 있 습 니까?(1)function 은 매번 첫 줄 에서 시작 되 고 generator 는 지난번 yield 에서 시 작 된 곳 에서 실 행 됩 니 다.
(2)function 호출 은 한 번(한 그룹)값 을 되 돌려 주 고 generator 는 여러 번 되 돌려 줍 니 다.
(3)function 은 수 차례 반복 되 지 않 고 호출 될 수 있 으 며,generator 인 스 턴 스 는 yield 의 마지막 값 이나 return 이후 에 계속 호출 할 수 없습니다.
함수 에서 Yild 를 사용 하고 이 함 수 를 호출 하 는 것 은 generator 를 생 성 하 는 방식 입 니 다.또 다른 흔 한 방법 은 사용
generator expression
,For example:
>>> gen = (x * x for x in xrange(5))
>>> print gen
<generator object <genexpr> at 0x02655710>
generator 응용generator 기초 응용
왜 generator 를 사용 합 니까?가장 중요 한 이 유 는 필요 에 따라 모든 반환 값 을 한꺼번에 만 드 는 것 이 아니 라'모든 반환 값'을 전혀 모 르 기 때 문 입 니 다.
예 를 들 어 아래 코드 에 대해 서...
RANGE_NUM = 100
for i in [x*x for x in range(RANGE_NUM)]: # :
# do sth for example
print i
for i in (x*x for x in range(RANGE_NUM)): # : generator
# do sth for example
print i
위의 코드 에서 두 개의 for 문 구 는 출력 이 같 습 니 다.코드 는 글자 그대로 보면 괄호 와 작은 괄호 의 차이 입 니 다.그러나 이 차이 점 은 매우 크다.첫 번 째 방법 반환 값 은 하나의 목록 이 고 두 번 째 방법 은 generator 대상 을 되 돌려 준다.RANGENUM 이 커지 면 첫 번 째 방법 으로 되 돌아 오 는 목록 도 커지 고 사용 하 는 메모리 도 커진다.하지만 두 번 째 방법 에 대해 서 는 별 차이 가 없다.우 리 는'되 돌아 갈 수 있다'는 무한 한 예 를 다시 보 았 다.
def fib():
a, b = 1, 1
while True:
yield a
a, b = b, a+b
이 generator 는 수많은'반환 값'을 생 성 하 는 능력 을 가지 고 있 으 며,사용 자 는 언제 교 체 를 중단 할 지 스스로 결정 할 수 있다.generator 고급 응용
사용 필드 1:
Generator 는 데이터 흐름 을 만 드 는 데 사용 할 수 있 습 니 다.generator 는 즉시 반환 값 을 만 들 지 않 고 필요 할 때 반환 값 을 만 들 수 있 습 니 다.이 는 주동 적 으로 끌 어 올 리 는 과정(pull)에 해당 합 니 다.예 를 들 어 현재 로그 파일 이 있 고 줄 마다 기록 이 생 깁 니 다.각 기록 에 대해 서로 다른 부서 의 사람들 이 처리 하 는 방식 이 다 를 수 있 지만 우 리 는 공용 을 제공 할 수 있 습 니 다.필요 에 따라 생 성 된 데이터 흐름.
def gen_data_from_file(file_name):
for line in file(file_name):
yield line
def gen_words(line):
for word in (w for w in line.split() if w.strip()):
yield word
def count_words(file_name):
word_map = {}
for line in gen_data_from_file(file_name):
for word in gen_words(line):
if word not in word_map:
word_map[word] = 0
word_map[word] += 1
return word_map
def count_total_chars(file_name):
total = 0
for line in gen_data_from_file(file_name):
total += len(line)
return total
if __name__ == '__main__':
print count_words('test.txt'), count_total_chars('test.txt')
위의 예 는 08 년 피 콘 의 한 강좌 에서 나 왔 다.4567914)데이터 생산자 이 고 countwords count_total_chars 는 데이터 소비자 입 니 다.데 이 터 는 필요 할 때 만 끌 어 올 리 는 것 이지 미리 준비 한 것 이 아니 라 는 것 을 알 수 있다.다른 genwords 에서gen_words gen_data_from_file
도 generator 가 생 겼 어 요.사용 필드 2:
일부 프로 그래 밍 장면 에서 한 가지 일 은 일부 논 리 를 실행 한 다음 에 한 동안 기다 리 거나 특정한 비동기 의 결 과 를 기다 리 거나 특정한 상 태 를 기다 린 다음 에 다른 논 리 를 계속 집행 해 야 할 수도 있다.예 를 들 어 마이크로 서비스 구조 에서 서비스 A 가 논 리 를 집행 한 후에 서비스 B 에 데 이 터 를 요청 한 다음 에 서비스 A 에서 계속 집행 한다.또는 게임 프로 그래 밍 에서 하나의 스 킬 은 여러 단계 로 나 뉘 어 일부 동작(효과)을 실행 한 다음 에 시간 을 기다 린 다음 에 계속 합 니 다.기 다 려 야 하고 막 히 기 를 원 하지 않 는 상황 에 대해 우 리 는 일반적으로 리 셋(callback)방식 을 사용한다.다음은 간단 한 예 를 들 어 보 겠 습 니 다.
def do(a):
print 'do', a
CallBackMgr.callback(5, lambda a = a: post_do(a))
def post_do(a):
print 'post_do', a
여기 있 는 CallBackMgr 은 5s 후의 시간 을 등록 하고 5s 후에(w for w in line.split() if w.strip())
함 수 를 호출 합 니 다.이 를 통 해 논리 가 두 함수 로 분 단 된 것 을 알 수 있 고 문맥 의 전달 도 필요 합 니 다(예 를 들 어 여기 있 는 매개 변수 a).이 예 를 yield 로 수정 합 니 다.yield 반환 값 은 기다 리 는 시간 을 의미 합 니 다.
@yield_dec
def do(a):
print 'do', a
yield 5
print 'post_do', a
이 decrator 를 통 해 이 generator 를 YieldManager 에 등록 하고 5s 후에 next 방법 을 호출 해 야 합 니 다.Yield 버 전 은 리 셋 과 같은 기능 을 실 현 했 지만 훨씬 뚜렷 해 보 였 다.다음은 참고 로 간단 한 실현 을 제공 합 니 다.
# -*- coding:utf-8 -*-
import sys
# import Timer
import types
import time
class YieldManager(object):
def __init__(self, tick_delta = 0.01):
self.generator_dict = {}
# self._tick_timer = Timer.addRepeatTimer(tick_delta, lambda: self.tick())
def tick(self):
cur = time.time()
for gene, t in self.generator_dict.items():
if cur >= t:
self._do_resume_genetator(gene,cur)
def _do_resume_genetator(self,gene, cur ):
try:
self.on_generator_excute(gene, cur)
except StopIteration,e:
self.remove_generator(gene)
except Exception, e:
print 'unexcepet error', type(e)
self.remove_generator(gene)
def add_generator(self, gen, deadline):
self.generator_dict[gen] = deadline
def remove_generator(self, gene):
del self.generator_dict[gene]
def on_generator_excute(self, gen, cur_time = None):
t = gen.next()
cur_time = cur_time or time.time()
self.add_generator(gen, t + cur_time)
g_yield_mgr = YieldManager()
def yield_dec(func):
def _inner_func(*args, **kwargs):
gen = func(*args, **kwargs)
if type(gen) is types.GeneratorType:
g_yield_mgr.on_generator_excute(gen)
return gen
return _inner_func
@yield_dec
def do(a):
print 'do', a
yield 2.5
print 'post_do', a
yield 3
print 'post_do again', a
if __name__ == '__main__':
do(1)
for i in range(1, 10):
print 'simulate a timer, %s seconds passed' % i
time.sleep(1)
g_yield_mgr.tick()
주의사항:(1)Yield 는 끼 워 넣 을 수 없습니다!
def visit(data):
for elem in data:
if isinstance(elem, tuple) or isinstance(elem, list):
visit(elem) # here value retuened is generator
else:
yield elem
if __name__ == '__main__':
for e in visit([1, 2, (3, 4), 5]):
print e
위의 코드 는 내장 배열 의 모든 요소 에 접근 합 니 다.우리 가 원 하 는 출력 은 1,2,3,4,5 이 고 실제 출력 은 1 입 니 다. 2 5 。왜 일 까요?주석 에서 보 듯 이 visit 는 하나의lambda
이기 때문에 네 번 째 줄 은yield_dec
로 돌아 가 고 코드 도 이 generator 인 스 턴 스 가 교체 되 지 않 았 습 니 다.그럼 코드 를 바 꾸 고 이 임시 generator 를 교체 하면 됩 니 다.
def visit(data):
for elem in data:
if isinstance(elem, tuple) or isinstance(elem, list):
for e in visit(elem):
yield e
else:
yield elem
또는 python 3.3 에서 사용 할 수 있 습 니 다generator function
.이 문법 은pep380에 추 가 된 것 입 니 다.
def visit(data):
for elem in data:
if isinstance(elem, tuple) or isinstance(elem, list):
yield from visit(elem)
else:
yield elem
(2)generator function 에서 return 사용python doc 에 서 는 return 을 사용 할 수 있다 고 명확 하 게 언급 되 어 있 으 며,generator 가 여기까지 실 행 될 때 StopIteration 이상 을 던 집 니 다.
def gen_with_return(range_num):
if range_num < 0:
return
else:
for i in xrange(range_num):
yield i
if __name__ == '__main__':
print list(gen_with_return(-1))
print list(gen_with_return(1))
그러나generator object
의 return 은 반환 값 을 가 져 올 수 없습니다.
def gen_with_return(range_num):
if range_num < 0:
return 0
else:
for i in xrange(range_num):
yield i
위의 코드 가 잘못 보 고 될 수 있 습 니 다.yield from
총결산이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
로마 숫자를 정수로 또는 그 반대로 변환그 중 하나는 로마 숫자를 정수로 변환하는 함수를 만드는 것이었고 두 번째는 그 반대를 수행하는 함수를 만드는 것이었습니다. 문자만 포함합니다'I', 'V', 'X', 'L', 'C', 'D', 'M' ; 문자열이 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.