Python yield 사용 에 대한 분석
다음으로 전송:http://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/index.html?ca=drs-
파 이 썬 을 처음 배 운 개발 자 들 은 많은 파 이 썬 함수 에서 yield 키 워드 를 사용 한 것 을 자주 발견 합 니 다. 그러나 yield 를 가 진 함수 의 실행 절 차 는 일반 함수 와 다 릅 니 다. yield 는 도대체 무엇 을 하 는 것 입 니까? 왜 yield 를 디자인 해 야 합 니까?본 고 는 yield 의 개념 과 용법 을 간단명료 하 게 설명 하여 독자 들 이 Python 에서 yield 의 간단 하고 강력 한 기능 을 체험 하도록 도와 줄 것 이다.
yield 를 가 진 함수 가 Python 에서 generator (생 성기) 라 고 부 르 는데 generator 가 무엇 입 니까?
우 리 는 먼저 generator 를 버 리 고 흔히 볼 수 있 는 프로 그래 밍 문제 로 yield 의 개념 을 보 여 줍 니 다.
피 보 나치 수열 피 보 나치 수열 을 어떻게 생 성 하 는 지 는 매우 간단 한 귀속 수열 로 첫 번 째 와 두 번 째 수 를 제외 하고 임의의 수 는 앞의 두 수 에서 더 할 수 있다.컴퓨터 프로그램 으로 피 보 나치 수열 의 앞 N 개 수 를 출력 하 는 것 은 매우 간단 한 문제 로 많은 초보 자 들 이 다음 과 같은 함 수 를 쉽게 쓸 수 있다.
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a+b
n += 1
fab (5) 를 실행 하면 다음 과 같은 출력 을 얻 을 수 있 습 니 다. >>> fab(5)
1
1
2
3
5
결 과 는 문제 가 없 지만 경험 이 있 는 개발 자 들 은 fab 함수 에서 직접 print 로 숫자 를 인쇄 하면 이 함수 의 재 활용 성 이 떨 어 집 니 다. fab 함수 가 None 으로 돌아 가기 때문에 다른 함수 들 은 이 함수 가 생 성 한 수열 을 얻 을 수 없습니다.fab 함수 의 재 활용 성 을 높이 려 면 수열 을 직접 인쇄 하지 않 고 List 로 돌아 가 는 것 이 좋 습 니 다.다음은 fab 함수 개작 후의 두 번 째 버 전 입 니 다.
def fab(max):
n, a, b = 0, 0, 1
L = []
while n < max:
L.append(b)
a, b = b, a + b
n = n + 1
return L
fab 함수 가 되 돌아 오 는 List 를 다음 과 같이 출력 할 수 있 습 니 다. >>> for n in fab(5):
... print n
...
1
1
2
3
5
개 작 된 fab 함 수 는 List 를 되 돌려 서 재 활용 요 구 를 만족 시 킬 수 있 지만 경험 이 있 는 개발 자 는 이 함수 가 실행 중 사용 하 는 메모리 가 매개 변수 max 의 증가 에 따라 커진다 고 지적 할 것 입 니 다. 메모리 사용량 을 제어 하려 면 List 를 사용 하지 않 는 것 이 좋 습 니 다.중간 결 과 를 저장 하 는 것 이 아니 라 iterable 대상 을 통 해 교체 합 니 다.예 를 들 어 Python 2. x 에서 코드:
for i in range(1000): pass
1000 개의 요 소 를 생 성 하 는 List 를 가 져 올 수 있 습 니 다. 코드:for i in xrange(1000): pass
1000 개의 요소 의 List 를 만 들 지 않 고 매번 교체 할 때마다 다음 수 치 를 되 돌려 주 며 메모리 공간 이 작 습 니 다.xrange 는 List 를 되 돌려 주지 않 고 iterable 대상 을 되 돌려 주기 때 문 입 니 다.iterable 을 이용 하여 우 리 는 fab 함 수 를 iterable 을 지원 하 는 class 로 바 꿀 수 있 습 니 다. 다음은 세 번 째 버 전의 Fab 입 니 다.
class Fab(object):
def __init__(self, max):
self.max = max
self.n, self.a, self.b = 0, 0, 1
def __iter__(self):
return self
def next(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n = self.n + 1
return r
raise StopIteration()
Fab 클래스 는 next () 를 통 해 수열 의 다음 수 를 계속 되 돌려 줍 니 다. 메모리 사용량 은 항상 상수 입 니 다. >>> for n in Fab(5):
... print n
...
1
1
2
3
5 >>> for n in Fab(5):
... print n
...
1
1
2
3
5
그러나 클 라 스 가 고 친 이 버 전 을 사용 하면 코드 는 1 판 fab 함수 보다 훨씬 간결 하지 않 습 니 다.만약 에 우리 가 1 판 fab 함수 의 간결 성 을 유지 하 는 동시에 iterable 의 효 과 를 얻 으 려 면 yield 가 도움 이 될 것 입 니 다.def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n = n + 1
네 번 째 버 전의 fab 는 1 판 에 비해 print b 를 yield b 로 바 꾸 는 것 만으로 간결 성 을 유지 하 는 동시에 iterable 효 과 를 얻 었 다.4 판 fab 와 2 판 fab 가 완전히 일치 하도록 호출 합 니 다.
>>> for n in fab(5):
... print n
...
1
1
2
3
5
쉽게 말 하면 yield 의 역할 은 하나의 함 수 를 generator 로 바 꾸 는 것 입 니 다. yield 를 가 진 함 수 는 더 이상 일반 함수 가 아 닙 니 다. Python 해석 기 는 이 를 generator 로 보고 fab (5) 를 호출 하면 fab 함 수 를 실행 하지 않 고 iterable 대상 으로 돌아 갑 니 다!for 순환 이 실 행 될 때 매번 순환 할 때마다 fab 함수 내부 의 코드 를 실행 합 니 다. yield b 로 실 행 될 때 fab 함 수 는 교체 값 을 되 돌려 줍 니 다. 다음 교체 시 코드 는 yield b 의 다음 문구 에서 계속 실 행 됩 니 다. 함수 의 로 컬 변 수 는 지난번 실행 중단 전과 똑 같 아서 함수 가 다시 yield 를 만 날 때 까지 계속 실 행 됩 니 다.fab (5) 의 next () 방법 (fab (5) 은 generator 대상 이기 때문에 이 대상 은 next () 방법 을 가지 고 있 습 니 다. 그러면 우 리 는 fab 의 실행 절 차 를 더욱 분명하게 볼 수 있 습 니 다.
>>> f = fab(5)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
>>> f.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
함수 실행 이 끝 났 을 때 generator 는 자동 으로 StopIteration 이상 을 던 져 교체 가 완료 되 었 음 을 나타 낸다.for 순환 에 서 는 StopIteration 이상 을 처리 하지 않 아 도 순환 이 정상적으로 끝 납 니 다.우 리 는 다음 과 같은 결론 을 얻 을 수 있다.
yield 가 있 는 함 수 는 generator 입 니 다. 일반 함수 와 달리 generator 를 만 드 는 것 은 함수 호출 처럼 보이 지만 함수 코드 를 실행 하지 않 습 니 다. next () 를 호출 할 때 까지 (for 순환 에서 next () 를 자동 으로 호출 합 니 다.실행 절 차 는 함수 의 절차 에 따라 실행 되 지만 하나의 yield 문 구 를 실행 할 때마다 중단 되 고 교체 값 을 되 돌려 줍 니 다. 다음 에 실행 할 때 yield 의 다음 문 구 를 계속 실행 합 니 다.한 함수 가 정상적으로 실행 되 는 과정 에서 yield 에 의 해 수 차례 중단 되 었 고 중단 할 때마다 yield 를 통 해 현재 의 교체 값 을 되 돌려 주 는 것 처럼 보 입 니 다.
yield 의 장점 은 분명 하 다. 한 함 수 를 하나의 generator 로 바 꾸 면 교체 능력 을 얻 을 수 있다. 클래스 의 인 스 턴 스 저장 상태 로 다음 next () 의 값 을 계산 하 는 것 보다 코드 가 간결 할 뿐만 아니 라 실행 절차 도 매우 뚜렷 하 다.
어떻게 함수 가 특수 한 generator 함수 인지 판단 합 니까?isgenerator function 을 이용 하여 판단 할 수 있 습 니 다.
>>> from inspect import isgeneratorfunction
>>> isgeneratorfunction(fab)
True
fab 와 fab (5) 를 구분 해 야 합 니 다. fab 는 generator function 이 고 fab (5) 는 fab 를 호출 하여 돌아 오 는 generator 입 니 다. 예 를 들 어 클래스 의 정의 와 클래스 의 인 스 턴 스 의 차이 와 같 습 니 다. >>> import types
>>> isinstance(fab, types.GeneratorType)
False
>>> isinstance(fab(5), types.GeneratorType)
True
fab 는 교체 할 수 없고 fab (5) 는 교체 할 수 있다. >>> from collections import Iterable
>>> isinstance(fab, Iterable)
False
>>> isinstance(fab(5), Iterable)
True
매번 fab 함 수 를 호출 할 때마다 새로운 generator 인 스 턴 스 를 생 성 합 니 다. 각 인 스 턴 스 는 서로 영향 을 주지 않 습 니 다. >>> f1 = fab(3)
>>> f2 = fab(5)
>>> print 'f1:', f1.next()
f1: 1
>>> print 'f2:', f2.next()
f2: 1
>>> print 'f1:', f1.next()
f1: 1
>>> print 'f2:', f2.next()
f2: 1
>>> print 'f1:', f1.next()
f1: 2
>>> print 'f2:', f2.next()
f2: 2
>>> print 'f2:', f2.next()
f2: 3
>>> print 'f2:', f2.next()
f2: 5
return 의 역할 은 generator function 에 있 습 니 다. return 이 없 으 면 함수 가 끝 날 때 까지 기본 으로 실 행 됩 니 다. 실행 과정 에서 return 이 있 으 면 StopIteration 을 던 져 교체 가 종 료 됩 니 다.
다른 예, 다른 yield 의 예 는 파일 읽 기 에서 비롯 됩 니 다.파일 대상 에 게 read () 방법 을 직접 호출 하면 예측 할 수 없 는 메모리 사용량 을 초래 할 수 있 습 니 다.좋 은 방법 은 고정된 길이 의 버퍼 를 이용 하여 파일 내용 을 계속 읽 는 것 이다.yield 를 통 해 우 리 는 더 이상 파일 을 읽 는 교체 클래스 를 작성 하지 않 아 도 파일 을 쉽게 읽 을 수 있 습 니 다.
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
이상 은 yield 의 기본 개념 과 용법 만 간단하게 소 개 했 을 뿐 yield 는 Python 3 에서 더욱 강력 한 용법 이 있 습 니 다. 우 리 는 후속 글 에서 토론 할 것 입 니 다.주: 본 논문 의 코드 는 모두 Python 2.7 에서 디 버 깅 을 통 과 했 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.