List Comprehension
List comprehension is an expression(표현식)
that transforms a collection (not necessarily a list) into alist
.
list
를 생성하는 expression 으로, 원본이 되는 collection 의 모든 item 혹은 일부 item들에 대해 같은 operation을 적용하여 새로운 list
(사실은 collection
또는 generator
) 를 생성한다.
- 사실
list comprehension
은 일종의 shorthand expression으로 중첩된 여러 반복문(loop) 및 조건문(if)으로 collection을 생성하는 것을 한 줄로 작성가능하게 해준다. list comprehension
은collection
을 입력으로 받고,- 해당
collection
의item
들에 대해 하나 하나씩 방문(iterate)하고 - 이들에 대해 지정된
boolean expression
으로 evaluation(True
인지False
인지 값을 얻는 처리)하여
일종의 filtering을 한 이후, - 지정된
loop expression
으로 transform을 수행한 결과 를 - 결과 collection에 item으로 추가 한다.
이를 글로 간단히 정리한 list comprehension
은 동작 과정은 다음과 같음.
- list-comprehension expression은 원본이 되는 collection 에 대해 iteration을 수행하여 각 item을 얻어옴.
- 얻어진 item들에 대해 optional
Boolean
expression 이 수행(evaluation)됨. - 해당
Boolean
expression이True
인 경우,
loop expression이 해당 item에 대해 수행되어 값이 얻어지고(=loop expression을 통해 transform이 이루어짐),
이 값이 결과list
에 item으로 추가됨. - 해당
Boolean
표현식이False
인 경우, 해당 itme은 무시됨.
그 외의 Comprehensions
사실comprehension
은 list
외의 collection들,
set
, dict
, tuple
들에도 적용가능하다
tuple
의 경우는 엄밀하게 애기하면 generator
임.
Generator Comprehension
tuple
로 comprehension을 만들 경우,
generator
를 반환해주는데
이를 generator expression
이라고도 부른다.
list-comprehension에서 square bracket을 parenthesis (or round bracket)으로만 바꾸면 된다.
generator
의 장점은 메모리를 효율적으로 사용한다는 점임.
yield
를 이용한 방식보다 tuple을 통한 comprehension으로 generator를 구현한 경우는
function이 아닌 expression
으로 구현하기 때문에 표현이 보다 심플해진다.
때문에 복잡한 로직의 generator
구현에는 generator expression
보다는 yield
를 통한 generator function
이 보다 낫다.
list-comprehension 문법
- 앞서 설명한 loop expression 은 comprehension을 사용하지 않은 경우에는 반복문 내부에 들어갈 expression(표현식)이고,
- boolean expression은 원본이 되는
collection
의 item들 중 어떤 것들을loop exression
으로 넘길지 말지를 결정하는 일종의 filter 역할을 수행하는True
와False
를 반환하는 expression(표현식)이다. (True
를 반환하는 item만이loop express
로 넘겨짐)
이들이 list comprehension 구문에서 놓여지는 위치는 다음과 같다.
ret_list = [ loop_expression for x in src_list if boolean_expression ]
- 참고로 위와같이 square bracket 을 사용하면 결과 collection이
list
로 결정된다.- 만일 curly bracket 이라면
set
또는dict
가 된다. (loop exression
부분에key : value
형태로 놓이면dict
, 단일 값의 expression이면set
) - 당연히 parenthesis 라면
tuple
에 해당하며, 이 경우는 결과tuple
에 해당하는generator
가 만들어진다.
- 만일 curly bracket 이라면
- 위의 예에서는
for
문에서x
변수를 사용하였기 때문에src_list
(원본 list)의 item들이 iterate되면서x
에 할당된다. - 이후, boolean_expression에서 현재
x
에 대해 조건을 확인하고True
시loop expression
으로 넘겨짐. boolean_expression
은 생략가능하며, 생략시src_list
의 모든 item 들이loop expression
으로 넘겨짐.
Examples
다음의 예제 코드를 살펴보면서 list-comprehension의 사용방법을 익숙해지도록 한다.
# src_list를 shallow copy.
# ret_list = src_list.copy() 또는 ret_list = src_list[:] 와 같은 결과임.
# 이 경우엔 comprehension을 사용하는 건 상대적으로 비효율적이라 이런 방식으로 거의 사용되지 않음.
ret_list = [x for x in src_list]
# non-negative filter를 적용하여 ret_list를 생성.
ret_list = [x for x in src_list if x >= 0]
# 3제곱 (cube of a number) 처리를 한 ret_list생성.
ret_list = [x**3 for x in src_list]
# 역수(valid reciprocal) 처리 (단 0으로 나누어지는 경우 제외)
ret_list = [1/x for x in src_list if x != 0]
# multi_line text로 구성된 file을 읽어들여 공백 line을 제거.
ret_lit = [line for line in [l.strip() for l in infile] if line != ""]
- 많이 사용되면서 해석하기 어렵지 않은 예제들임.
- 단, 맨 마지막과 같이 list comprehension 들을 중첩시키는 것도 가능하다는 것을 명심할 것.
- 개인적으로 2회 이상 중첩되는 comprehension은 가독성이 너무 떨어진다. (가독성 측면에서 comprehension에서 중첩은 한번으로 족하다.)
- 사실 comprehension을 중첩시키지 않고, comprehension 내의
for
문을 여러번 사용할 수도 있으나 이것도 2회 이상의 중첩이 필요하다면 일반 반복문으로 구현하는게 좋다.
list comprehension에서, loop expressoin 이나 boolean expression 이 각각 2회 초과하여 사용되는 경우도 권장되지 않는다.
이중 for 문
다음은 구구단의 결과값을 가지는 일렬로 가지는 list
임.
ret_list = [ i * j for i in range(2,10) for j in range(1,10)]
# 위의 코드를 일반 반복문으로 구현
ret_list0 = []
for i in range(2,10):
for j in range(1,10):
ret_list0.append(i*j)
print(ret_list)
print(ret_list0)
앞에 있는 for문이 바깥쪽이고 뒤의 for문이 안쪽에 위치함.
matrix등의 접근
많이 사용되는 처리방식은 아니지만, list comprehension에 대한 이해에 도움이 되는 예제임.
m = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
m_ravel = [x for row in m for x in row]
- 중첩된 list를 flattening한 결과를 얻음.
boolean expression 여러개 사용하기.
2 또는 3의 배수만을 추출하여 결과 list를 만드는 예제는 다음과 같음.
ret_list = [x for x in range(1,101) if x % 2 == 0 or x % 3 == 0]
권장하지는 않지만 if
를 연속으로 기재하는 경우 and
로 처리된다 (가독성 때문에 권하지 않음).
아래 예제는 2의 배수이면서 동시에 3의 배수인 수들을 추출한다.
ret_list = [x for x in range(1,101) if x % 2 == 0 if x % 3 ==0]
- 이전의 예제가
or
로 명시적으로 처리된 것과 달리 위의 경우는and
로 처리됨.
재밌는 건 위와 같이 2개의
if
문 사이에or
를 넣을 경우SyntaxError
가 발생한다.
다음이 같은 결과를 내는 다른 방법으로 단일 if문에 복수의 조건을 사용했다 (권장).
ret_list = [x for x in range(1,101) if x%2 == 0 and x % 3 == 0]
같이 보면 좋은 자료들
'Programming' 카테고리의 다른 글
[Python] range and enumerate (0) | 2023.06.07 |
---|---|
[Python] Iterable and Iterator, plus Generator (1) | 2023.06.07 |
[Python] Assignment와 Shallow Copy, Deep Copy (0) | 2023.06.05 |
[Programming] Garbage Collection (GC) (0) | 2023.06.05 |
[Python] Interpreter and PVM (Python Virtual Machine) (1) | 2023.06.05 |