티스토리 뷰
🚀 들어가며...
- 파이썬 List 형식의 강력한 특징이라고 할 수 있는 List Comprehension과 그와 비슷한 형식인 Generator에 대해 알아보고, 두 가지 방식의 차이점에 대해 알아보겠습니다!!
📑 내용
1. List Comprehension이란?
Python 에서는 리스트 구성을 위해 항목을 하나하나 나열하는 방식외에 구문을 통해 sequence 형태의 데이터를 가공하여 리스트를 구성하는 방법을 제공하고 있습니다.
쉽게 말해서, list를 쉽고 빠르게 만들 수 있는 방법입니다.
예시를 들어보겠습니다. 1부터 10까지의 숫자의 제곱 중 홀수 만을 추출하는 것을 만들어봅시다.
고차원 함수를 이용하면,
#고차원 함수 이용
numbers=[1,2,3,4,5,6,7,8,9,10]
x=map(lambda n:n*n, filter(lambda n:n%2==1, numbers))
x #[1, 9, 25, 49, 81]
이렇게 표현할수 있을 것입니다.
똑같은 내용을 List comprehension을 사용하면,
#List Comprehension 이용
x=[n*n for n in numbers if n%2==1]
x #[1, 9, 25 49, 81]
이렇게 표현 가능합니다.
즉, list comprehension은 for in if 를 적절히 사용하여
코드 한 줄에 list를 빠르게 만들 수 있는 편리하고 강력한 방법입니다.
#list comprehension 활용 예시
P1=[x**2 for x in range(10)]
P1 #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
P2=[2**i for i in range(13)]
P2 #[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
P3=[x for x in P1 if x%2==0]
P3 #[0, 4, 16, 36, 64]
list comprehension 사용에 익숙해 진다면 리스트 안에 튜플을 넣거나,
그 튜플 중 특정 위치에 있는 원소만 뽑아서 다시 list로 만드는 등
리스트를 자유자재로 활용할 수 있어 매우 편리합니다.
2. Generator Expression이란?
"generator expression"은 python 2.6. 에서 소개되었습니다.
이것은 list comprehension과 syntax나 작동하는 방법 면에서 비슷하지만,
list대신 generator를 반환한다는 점에서 크게 다르다고 할 수 있습니다.
제너레이터는 모든 값을 메모리에 담고 있지 않고 그때그때 값을 생성(generator)해서 반환하기 때문에
제너레이터를 사용할 때에는 한 번에 한 개의 값만 순환(iterate)할 수 있는 특징이 있습니다.
- iterator : next() 메서드를 이용해 데이터에 순차적으로 접근이 가능한 객체
3. List Comprehension과 Generator Expression의 차이
generator 는 일반적인 함수와 비슷해보이지만, 가장 큰 차이점은 yield 라는 구문일 것 같습니다. yield는 무엇일까요?
- yield 는 generator가 일반 함수와 구분되는 가장 핵심적인 부분입니다.
- 일반적인 함수의 경우, 사용이 종료되면 결과 값을 호출부로 반환한 뒤 함수 자체를 종료시킨 후 메모리 상에서 정리(clear)되지만 yield는 그렇지 않습니다. generator 함수가 실행 중에 yield를 만날 경우, 해당 함수는 그 상태로 정지되며, 반환 값을 next()를 호출한 쪽으로 전달합니다. 이후 해당 함수는 일반적인 경우처럼 종료되는 것이 아니라 그 상태로 유지되게 됩니다. 즉, 함수에서 사용된 로컬 변수나 instruction pointer 등과 같은 함수 내부에서 사용된 데이터들이 메모리에 그대로 유지됩니다.
예시를 통해 알아보겠습니다.
def generator(n):
i = 0
while i < n:
yield i
i += 1
for x in generator(5):
print(x)
# 출력
0
1
2
3
4
- for 문이 실행되며 generator 함수가 호출됩니다.
- generator 함수는 일반 함수와 동일한 절차로 실행됩니다.
- 실행 중 while 문 안에서 yield를 만나게 됩니다. 그러면 return 과 비슷하게 함수를 호출했던 구문으로 반환하게 됩니다. 여기서는 첫번째 i 값인 0을 반환합니다. 하지만 반환했다고 generator 함수가 종료되는 것이 아니라 그대로 유지한 상태입니다.
- x 값에는 yield에서 전달된 0 값이 저장된 후 출력됩니다. 그 후 for 문에 의해 다시 generator 함수가 호출됩니다.
- 이 때, generator 함수는 처음부터 시작되는 것이 아니라 yield 이후부터 시작됩니다. 따라서 "i += 1" 문장이 실행되고 i 값은 1로 증가합니다.
- 아직 while 문 내부이기 때문에 yield 구문을 만나 i 값인 1이 전달됩니다.
- x 값은 1을 전달 받고 출력됩니다. (이후 반복)
4. Generator Expression을 사용하는 이유
generator expression을 사용하는 이유는 제 생각에는 시간과 메모리 때문인 것 같습니다. 아래 예시를 들어보며 설명해보겠습니다.
1. 메모리를 효율적으로 사용할 수 있습니다.
import sys
# list
sys.getsizeof( [i for i in range(100) if i % 2] )
536
sys.getsizeof( [i for i in range(1000) if i % 2] )
4280
# generator
sys.getsizeof( (i for i in range(100) if i % 2) )
80
sys.getsizeof( (i for i in range(1000) if i % 2) )
80
list의 경우 사이즈가 커질수록 메모리 사용량이 늘어남. 반면 generator의 경우 사이즈가 커져도 차지하는 메모리 사이즈는 동일합니다.
- list는 list 안에 속한 모든 데이터를 메모리에 적재하기 때문에 list의 크기만큼 차지하는 메모리 사이즈가 늘어나게 됩니다. 반면 generator는 데이터 값을 한꺼번에 메모리에 적재하는 것이 아니라 next() 메서드를 통해 차례로 값에 접근할 때마다 메모리에 적재합니다. 따라서 list의 규모가 큰 값을 다룰수록 generator의 효율성은 높아집니다.
2. Lazy Evaluation 효과를 볼 수 있습니다. (시간)
- Lazy Evaluation: 계산 결과 값이 필요할 때까지 계산을 늦추는 효과를 말합니다.
- 예제 sleep_func()는 1초간 sleep을 수행한 뒤 x 값을 반환하는 함수라고 한다면, 만약 위 sleep_func()함수를 이용해 list와 generator를 생성하면 어떻게 동작하는지 비교해보겠습니다.
- list 예제
list = [sleep_func(x) for x in range(5)]
for i in list:
print(i)
# 출력
sleep...
sleep...
sleep...
sleep...
sleep...
0
1
2
3
4
- generator 예제
gen = (sleep_func(x) for x in range(5))
for i in gen:
print i
# 출력
sleep...
0
sleep...
1
sleep...
2
sleep...
3
sleep...
4
list의 경우, list의 모든 값을 먼저 수행하기 때문에 sleep_func() 함수를 range() 값만큼 한번에 수행합니다. 만일 sleep_func()에서 수행하는 시간이 길거나 list값이 매우 큰 경우 처음 수행할 때 그만큼 부담으로 작용됩니다.
반면, generator의 경우 generator 생성 시 실제 값을 로딩하지 않고, for 문이 수행될 때 하나씩 sleep_func()를 수행하며 값을 불러오게 됩니다. 수행 시간이 긴 연산을 필요한 순간까지 늦출 수 있다는 점이 특징입니다.
이러한 특징을 이용하면, fibonacci 수열과 같은 작업을 간결한 문법과 더불어 매우 효율적으로 코드를 작성할 수 있습니다.
🙋🏻♂️ 후기
평소에는 List Comprehension을 많이 사용하고 generator는 잘 사용하지 않아서 generator에 대한 이해가 부족했었는데 이번에 정리하게 됨으로써 generator가 얼마나 효율적인지 알게 되었습니다.
여름휴가를 다녀온 직후라 평소보다 피곤한 상태지만 으쌰으쌰 힘내서 다시 열정모드로 돌아오도록 하겠습니다.
(공부목적으로 작성한 글로, 피드백은 언제나 환영입니다~!!)
🔗 참고한 글
https://bluese05.tistory.com/56
'Python' 카테고리의 다른 글
[python] 함수로 받아온 쿼리 결과를 json 형식으로 만드는 방법 (4) | 2022.09.01 |
---|---|
[python] 언더스코어(_, __) 에 대해 알아보자! (0) | 2022.06.27 |
[python] list 정렬시 특징 정리 (0) | 2022.05.24 |
[python] mutable, immutable 그리고 복사 (0) | 2022.05.23 |
[Python] 파이썬에서 json모듈 다루는 방법 (0) | 2022.05.12 |
- Total
- Today
- Yesterday
- django
- programmers
- list
- static files
- 탐욕법
- react
- This
- Algorithm
- ORM
- Python
- lv1
- MVT
- union-find
- uSWGI
- PostgreSQL
- db
- Default export
- generator expression
- django ORM
- SQL
- docker
- JavaScript
- lv2
- container
- Greedy Algorithm
- data formatting
- JS
- Linux
- Master & Slave
- Named export
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |