0. Introduction
- 병행성을 위한 방법으로 generator와 coroutine을 알아보고자 한다.
- 이번 포스팅에서는
generator
를 알아본다. - 그리고, 병행성과 병렬성이 개념적으로 무엇인지 학습한다.
- 두 번째로, Generator를 본격적으로 알아본다.
1. 병행성과 병렬성이란??
병행성(Concurrency): 한 컴퓨터가 여러 일을 동시에 수행하여, 단일 프로그램 안에서 여러 일을 쉽게 해결 목적
병렬성(parallelism): 여러 컴퓨터가 여러 작업을 동시에 수행하여, 속도 향상 목적
- 병행성 : thread는 하나지만, 마치 동시에 일을 하고 있는 것처럼 수행한다.
- 예) 공부 중에 강의 멈춰놓고, 밥 먹고 와서 강의를 중단한 부분부터 다시 시작하는 것
- 파이썬에서는 병행성 과 병렬성 을 모두 지원한다.
- 그리고, 파이썬 실력을 결정하는 중요한 내용이다.
2 Generator란?
- 모든 값을 메모리에 올려두고 이용하는 게 아닌,
- 필요할 때마다 한 번에 한 개의 항목을 생성해서 메모리에 올려두고 반환하는 객체.
- 그래서 메모리를 유지하지 않기 때문에, 효율적으로 사용할 수 있다.
Generator는 iterator의 한 종류로, 위와 같은 이유로 매우 강력한 iterator다.
연산을 필요한 순간까지 미루는 걸
Lazy evaluation
이라 한다.iterator이므로 출력하기 위해
next()
를 사용한다.Generator function이 일반 function과의 차이는
yield
statement다.Generator = iterator + yield
공통점:
yield
또한return
처럼 값을 반환한다.
차이점:
return
을 사용할 경우 지역 변수가 사라지지만,yield
는 local을 나가도 사라지지 않는다. 위치 인자를 계속해서 유지한다.- next처럼 ‘위치 인자’를 계속해서 유지하는 게 ‘병행성’의 핵심
- 그리고,
yield
는 제네레이터를 반환한다.
Generator의 장점
- list comprehension, dictionary comprehension 등 데이터 양이 증가하면 메모리 사용량이 증가하는데, 이 때 제네레이터를 사용하여 메모리 사용량을 줄이고, 수행시간도 절약해준다.
- 단위 실행 가능한 코루틴(Coroutine) 구현과 연동이 가능하다.
- 작은 메모리 조각으로 사용 가능하다.
Generator 주의사항
- generator는 실행 시, 함수의 몸체를 실행하는 게 아니라, generator 함수가 가진 객체를 반환하는 일을 한다.
- 한 번 생성해서 반환한 객체를 보관하지 않기 때문에, 이전 코드를 실행한 후, 추가 코드를 실행하면 아무런 객체도 출력되지 않는다.
3. Generator 예제
3.1 예제 1
|
|
yield
까지 출력한 후, 다음 출력은 다음yield
까지 한다.- 이처럼 위의 next처럼 ‘위치 인자’를 계속해서 유지하는 게 ‘병행성’의 핵심 이다.
위치 인자
를 계속해서 기억하는 것 즉, 다음 할 일을 계속해서 기억하는 걸 의미한다.
3.2 예제 2
- 이 예제가 단순히 동일한 일을 하는 것처럼 보이지만,
- 생성된 값을 미리 메모리에 만들어 두는 게 아닌,
- for 문에서 필요한 때마다 generator로부터 받아온다.
- 즉, 메모리에서 보관하지 않는다.
list comprehension
과 유사해보이지만,소괄호()
를 사용하여generator expression
을 만들 수 있다.
|
|
|
|
- 그러면 list comprehension과 generator를 더 자세히 비교해보자.
|
|
위 code review
- list comprehension은 함수를 미리 다 실행시켜서 ‘sleep 1s’ 문자열이 먼저 출력되었다.
- 하지만, generator는 실제로 값을 출력하기 전까지 실행하지 않았다.
- 값을 사용하는 순간에만 함수를 실행하고 있다.
이 내용을 더 자세히 확인해보자.
print_iter를 아래와 같이 수정한 후, 실행하자.
|
|
위 code review
- list comprehension:
- lazy_return 함수를 모두 실행한 후, print_iter 함수를 실행할 때 멈췄다.
- generator expression:
- print_iter 함수가 실행 시, lazy_return 함수를 실행한 걸 확인할 수 있다.
- list comprehension:
이번에는 속도를 비교해보자.
|
|
위 code review
- 제네레이터를 사용했을 때 시간이 단축되었다.
이번에는 메모리를 비교해보자.
|
|
- 데이터의 크기가 커질수록 제네레이터의 효율성이 더 두드러지게 나타난다!!
- 이렇게 어떤 값이 실제로 쓰일 때까지 그 값의 연산을 뒤로 미루는 방식을
Lazy Evaluation
이라 한다.
4. Generator 관련 중요 함수들
- Generator 관련 함수들은
itertools
를 import하는 것부터 시작한다. - 계속 복습을 하면서 활용해보도록 하자.
4.1 itertools.count(시작값, 증가값)
- 첫 번째는
itertools.count(시작값, 증가값)
이다.- 시작값에서 증가하여, 증가값만큼 커져서 무한히 출력된다.
|
|
4.2 itertools.takewhile(predicate, iter)
- 두 번째는
itertools.takewhile(predicate, iter)
다.- iter의 원소들 중 predicate의 조건에 참인 값들을 반환한다.
- predicate는 영어 단어 자체의 의미로는 영어 문법의 서술부를 의미한다.
|
|
4.3 itertools.filterfalse(predicate, iter)
- 세 번째는
itertools.filterfalse(predicate, iter)
이다.- 두 번째인
itertools.takewhile
과 반대 의미를 가진 함수다. - iter 원소들 중에서 predicate의 조건에 부정인 값들을 반환한다.
- 두 번째인
|
|
4.4 itertools.accumulate(iterable, func=operator.add)
- 네 번째는
itertools.accumulate(iterable, func=operator.add)
이다. - iterable의 누적 합계나, 다른 이항함수 func의 누적 결과를 반환하는 iterator를 만든다.
- 총 원소 수가 n개라고 할 때,
- iterable[0], iterable[0] + iterable[1], …, iterable[0]+ ~ + iterable[n-1]
|
|
4.5 itertools.chain(*iterables)
- 다섯 번째는
itertools.chain(*iterables)
이다. - 첫 번째 iterable에서 소진될 때까지 원소들을 반환한 후, 다음 이터러블로 넘어간다.
- 이런 식으로 iterables의 모든 iterable이 소진될 때까지 진행하는 iterator를 만든다.
|
|
4.6 itertools.product(*iterables, repeat=)
- 여섯 번째는
itertools.product(*iterables, repeat=1)
다. - 입력 이터러블들(iterables)의 데카르트 곱을 반환한다.
- 대략 제너레이터 표현식에서의 중첩된 for-루프와 동일하다.
- 예를 들어 product(A, B)는 ((x,y) for x in A for y in B)와 같은 것을 반환한다.
|
|
4.7 itertools.groupby(iterable, key = none)
- 일곱 번쨰는
itertools.groupby(iterable, key = none)
이다. - (분류기준, 분류기준으로 묶인 데이터) 순서인 tuple로 값을 반환한다.
|
|