Python에서 새 목록을 생성할 때 목록 이해 표기법을 사용하는 것은 간단합니다.(List comprehensions
)
- 5. Data Structures — List Comprehensions — Python 3.10.0 Documentation
- 6. Expressions — Displays for lists, sets and dictionaries — Python 3.10.0 Documentation
이 기사에서 우리는 먼저 다음을 논의 할 것입니다
- 목록 이해 표기법의 기본 유형
- if에 의한 조건부 분기가 있는 목록 이해 표기법
- 삼항 연산자와의 조합(else-like 처리인 경우)
zip()
,enumerate()
이들과의 조합- 중첩 목록 포함 표기법
다음으로 샘플 코드를 사용하여 목록 이해 표기법 집합에 대해 설명합니다.
- 포함 표기법 설정(
Set comprehensions
) - 사전 포함 표기법(
Dict comprehensions
) - 발전기 유형(
Generator expressions
)
목록 이해 표기법의 기본 유형
목록 이해 표기법은 다음과 같이 작성됩니다.
[Expression for Any Variable Name in Iterable Object]
목록, 튜플 또는 범위와 같은 반복 가능한 개체의 각 요소를 임의의 변수 이름으로 가져와 표현식으로 평가합니다. 평가 결과가 요소로 포함된 새 목록이 반환됩니다.
동일한 for 문과 함께 예제가 제공됩니다.
squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
squares.append(i**2)
print(squares)
# [0, 1, 4, 9, 16]
map()을 사용하여 동일한 프로세스를 수행할 수 있지만 단순성과 명확성을 위해 목록 이해 표기법이 선호됩니다.
if에 의한 조건부 분기가 있는 목록 이해 표기법
if로 조건부 분기도 가능합니다. 다음과 같이 접미사에 if를 쓰십시오.
[Expression for Any Variable Name in Iterable Object if Conditional Expression]
조건식이 true인 iterable 객체의 요소만 표현식에 의해 평가되고 요소가 결과인 새 목록이 반환됩니다.
조건식에는 모든 변수 이름을 사용할 수 있습니다.
동일한 for 문과 함께 예제가 제공됩니다.
odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
if i % 2 == 1:
odds.append(i)
print(odds)
# [1, 3, 5, 7, 9]
동일한 프로세스를 filter()로 수행할 수 있지만 단순성과 명확성을 위해 목록 이해 표기법이 선호됩니다.
삼항 연산자와의 조합(else-like 처리인 경우)
위의 예에서는 기준에 맞는 요소만 처리하고 기준에 맞지 않는 요소는 새 목록에서 제외합니다.
조건에 따라 프로세스를 전환하거나 if else와 같이 조건을 충족하지 않는 요소를 다르게 처리하려면 삼항 연산자를 사용합니다.
Python에서 삼항 연산자는 다음과 같이 작성할 수 있습니다.
Value When True if Conditional Expression else Value When False
이는 아래와 같이 목록 내포 표기법의 표현 부분에서 사용됩니다.
[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]
동일한 for 문과 함께 예제가 제공됩니다.
odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
if i % 2 == 1:
odd_even.append('odd')
else:
odd_even.append('even')
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
true 및 false 값에 대해 임의의 변수 이름을 사용하여 표현식을 작성할 수도 있습니다.
조건이 충족되면 일부 처리가 완료되고, 그렇지 않으면 원래 반복 가능한 객체의 값이 변경되지 않은 상태로 남습니다.
odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]
zip() 및 enumerate()와의 조합
for 문에서 자주 사용되는 유용한 함수로는 여러 iterable을 결합하는 zip()과 인덱스와 함께 값을 반환하는 enumerate()가 있습니다.
물론 목록 이해 표기법과 함께 zip() 및 enumerate()를 사용할 수 있습니다. 특별한 문법도 아니고 for문과의 대응을 생각해보면 어렵지 않습니다.
zip()의 예.
l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']
l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
l_zip.append((s1, s2))
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
enumerate()의 예.
l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
l_enu.append((i, s))
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
아이디어는 if를 사용할 때와 이전과 동일합니다.
l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]
각 요소를 사용하여 새 요소를 계산할 수도 있습니다.
l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]
l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]
중첩 목록 포함 표기법
루프 중첩과 마찬가지로 목록 이해 표기법도 중첩될 수 있습니다.
[Expression for Variable Name 1 in Iterable Object 1
for Variable Name 2 in Iterable Object 2
for Variable Name 3 in Iterable Object 3 ... ]
편의를 위해 줄 바꿈과 들여쓰기가 추가되었지만 문법에는 필요하지 않습니다. 한 줄에서 계속할 수 있습니다.
동일한 for 문과 함께 예제가 제공됩니다.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
for x in row:
flat.append(x)
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
여러 변수를 사용하는 것도 가능합니다.
cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
조건부 분기를 수행할 수도 있습니다.
cells = [(row, col) for row in range(3)
for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]
각 반복 가능한 개체에 대해 조건부로 분기하는 것도 가능합니다.
cells = [(row, col) for row in range(3) if row % 2 == 0
for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]
포함 표기법 설정(Set comprehensions)
목록 이해 표기법에서 대괄호 []를 중괄호 {}로 변경하면 집합(집합 유형 객체)이 생성됩니다.
{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}
print(s)
# {0, 1, 4, 9, 16}
사전 포함 표기법(Dict comprehensions)
사전(dict 유형 객체)도 이해 표기법으로 생성할 수 있습니다.
{}를 사용하고 표현식 부분의 키와 값을 key:value로 지정합니다.
{Key: Value for Any Variable Name in Iterable Object}
키 및 값에 대해 모든 표현식을 지정할 수 있습니다.
l = ['Alice', 'Bob', 'Charlie']
d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}
키와 값 목록에서 새 사전을 만들려면 zip() 함수를 사용합니다.
keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]
d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}
발전기 유형(Generator expressions)
목록 내포 표기법에서 대괄호 []를 대괄호()로 사용하면 튜플 대신 제너레이터가 반환됩니다. 이것을 제너레이터 표현식이라고 합니다.
목록 이해 표기법의 예.
l = [i**2 for i in range(5)]
print(l)
# [0, 1, 4, 9, 16]
print(type(l))
# <class 'list'>
생성기 표현식의 예. 제너레이터를 그대로 print()하면 내용을 출력하지 않지만 for문으로 실행하면 내용을 얻을 수 있다.
g = (i**2 for i in range(5))
print(g)
# <generator object <genexpr> at 0x10af944f8>
print(type(g))
# <class 'generator'>
for i in g:
print(i)
# 0
# 1
# 4
# 9
# 16
생성기 표현식은 목록 이해 표기법뿐만 아니라 if를 사용하여 조건부 분기 및 중첩도 허용합니다.
g_cells = ((row, col) for row in range(0, 3)
for col in range(0, 2) if col == row)
print(type(g_cells))
# <class 'generator'>
for i in g_cells:
print(i)
# (0, 0)
# (1, 1)
예를 들어, 목록 이해 표기법을 사용하여 많은 수의 요소가 있는 목록을 생성한 다음 for 문으로 반복하는 경우 목록 이해 표기법을 사용하면 모든 요소를 포함하는 목록이 처음에 생성됩니다. 반면에 제너레이터 표현식을 사용하면 루프가 반복될 때마다 요소가 하나씩 생성되기 때문에 메모리 사용량이 줄어듭니다.
생성기 표현식이 함수의 유일한 인수인 경우 대괄호()를 생략할 수 있습니다.
print(sum([i**2 for i in range(5)]))
# 30
print(sum((i**2 for i in range(5))))
# 30
print(sum(i**2 for i in range(5)))
# 30
처리 속도에 관해서는 모든 요소가 처리될 때 목록 이해 표기법이 생성기 표기법보다 빠른 경우가 많습니다.
그러나 예를 들어 all() 또는 any()로 판단할 때 결과는 false 또는 true가 있을 때 결정되므로 목록 이해 표기법을 사용하는 것보다 제너레이터 표현식을 사용하는 것이 더 빠를 수 있습니다.
튜플 이해 표기법은 없지만 tuple()의 인수로 제너레이터 표현식을 사용하면 이해 표기법으로 튜플을 생성할 수 있다.
t = tuple(i**2 for i in range(5))
print(t)
# (0, 1, 4, 9, 16)
print(type(t))
# <class 'tuple'>