본문 바로가기
Programming

Python List Comprehension 마스터 하기

by 코푸보이 2021. 9. 8.
반응형

개발자로 일하면서 대부분을 자바만 사용하다가 대학원에 와서 파이썬을 주로 사용하면서 적응이 안되는 것 중 하나가 파이썬의 List Comprehension 이었습니다. 동시에 빨리 이해해서 뭔가 Pythonic 한 코드를 작성하고 싶다고 느끼게 만들어 준 것도 List Comprehension 이였죠.

 

아직 완벽하게 이해해서 활용하고 있다고 보긴 어렵지만, 그래도 저 처럼 빨리 pythonic 해 지고 싶어하시는 분들을 위해 파이썬 List comprehension 마스터(라고 쓰고 맛보기라고 읽는다...) 하기 글을 작성합니다. 아래의 9가지 사용법에 대해서 익히셔서 부디 저와 함께 하루 빨리 List comprehension 를 능숙하게 사용할 수 있게 되시길 바라겠습니다.

 

1. 기본 구문 살펴보기

# list comprehension
[expression for item in iterable]

# expanded form
for item in iterable:
    expression

list comprehension은 위와 같습니다. 쉽게 생각해 for 문으로 돌아가면서 표현하거나 리스트로 만들 것을 List 문 내에서 (대개) 한 줄로 표현 하는 것 입니다. 간결함을 강조하고 주 무기로 하는 파이썬의 특성을 잘 보여주는 기능이기에 익숙해지면 익숙해 질 수록 점점 더 pythonic 한 코드를 작성할 수 있습니다. :)

 

2. 리스트 생성

List comprehension 을 사용하는 첫 번째 이유는 아무래도 간편하게 리스트를 생성하는데 있다고 할 수 있습니다. 만약 List comprehension 에 대해서 전혀 모르는 상태라고 한다면 아무래도 리스트를 만들기 위해 Java 에서 ArrayList 에 아이템을 add 하듯이 for 문을 사용하여 리스트를 생성했을 것 입니다.

 

하지만 위의 기본 구문에서도 보았듯이 List comprehension 을 사용하면 한줄의 코드로 리스트를 간편하게 생성 할 수 있습니다. 또한 아래와 같이 다른 리스트나 튜플(tuple) 의 아이템을 간편하게 수정해서 새로운 리스트를 만들 수도 있습니다.

pets = ('bird', 'snake', 'dog', 'turtle', 'cat', 'hamster')
uppercased_pets = [pet.upper() for pet in pets]

uppercased_pets
# printed result
['BIRD', 'SNAKE', 'DOG', 'TURTLE', 'CAT', 'HAMSTER']

 

3. 조건문을 이용한 필터링(Filtering)

리스트를 생성하기까지 잘 마치셨나요? 이번에는 이런 경우도 생각해 보시죠. 만약 리스트에서 특정 아이템만 뽑고 싶다면 어떻게 해야 할까요? Java 라면 for 문 안에서 if 문을 넣어두고, List 를 돌면서 List 안의 아이템이 저희가 for 문 안에 넣은 if 조건문에 걸리는지 보고, if 문에 맞는지(혹은 맞지 않는지) 체크하는 식으로 필터링 하게 되겠죠.

 

파이썬의 List Comprehension 은 위의 과정도 아래와 같이 한줄로 작성될 수 있도록 도와 줍니다.

primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
squared_primes = [x*x for x in primes if x%10 == 3]

squared_primes
# printed result
[9, 169, 529]

 

위의 처럼 간단한 조건문이 아니라면?

답은 간단합니다. 함수를 넣어주시면 됩니다.

def has_four_legs(pet):
    return pet in ('pig', 'dog', 'turtle', 'hamster', 'cat')

pets = ('bird', 'snake', 'dog', 'turtle', 'cat', 'hamster')
four_legs_pets = [pet.capitalize() for pet in pets if has_four_legs(pet)]

four_legs_pets
# printed result
['Dog', 'Turtle', 'Cat', 'Hamster']

 

Filtering 을 조금 더 꼬아서 if 문 뿐만 아니라 else 문 까지 사용해 볼까요?

아래의 numbers 의 아이템이 10 이하인 경우, 그대로 가져오고, 10 을 넘어가는 값인 경우에는 10을 넣도록 해보겠습니다.

max_value = 10
numbers = (7, 9, 11, 4, 3, 2, 12)

ceiling_numbers0 = [number if number <= max_value else max_value for number in numbers] 

ceiling_numbers0
#printed reuslt
[7, 9, 10, 4, 3, 2, 10]

ceiling_numbers1 = [(number if number <= max_value else max_value) for number in numbers]

ceiling_numbers1
#printed result
[7, 9, 10, 4, 3, 2, 10]

 

4. map() !?

파이썬의 map() 함수에 대해서 알고 계신가요? 혹시 map 이 어떤건지 잘 모르시는 분이라면 이 링크 를 참조해 보시기 바랍니다. 간단히 설명 드리자면 map 은 iterable 한 객체의 아이템들에 쉽게 funtion 을 적용시킬 수 있습니다. 위의 리스트 생성 예제에서 보여드렸던 리스트 내의 문자로 모두 대문자로 만들었던 예제를 다시 소환해 보겠습니다.

pets = ('bird', 'snake', 'dog', 'turtle', 'cat', 'hamster')
uppercased_pets = [pet.upper() for pet in pets]

uppercased_pets
#printed result
['BIRD', 'SNAKE', 'DOG', 'TURTLE', 'CAT', 'HAMSTER']

map 을 사용하면 다음과 같이 사용 할 수 있습니다.

pets = ('bird', 'snake', 'dog', 'turtle', 'cat', 'hamster')
uppercased_pets = list(map(str.upper, pets))

uppercased_pets
#printed result
['BIRD', 'SNAKE', 'DOG', 'TURTLE', 'CAT', 'HAMSTER']

어떻게 작성하는게 더 좋아보이시나요? 상황에 따라 map 과 list comprehension 중 어느 것을 사용하면 좋을지 잘 선택해서 사용하시기 바랍니다!

 

5. 중첩 List Comprehension

for 문을 사용해 보았으니 다음으로 이중 for 문을 한번 사용해 보겠습니다. 개인적으로 중첩 for 문을 list comprhension 을 통해서 사용하는 것은 코드 readability 측면에서 좋지 않다고 생각 합니다. 코드를 이해하기 너무 "귀찮" 기 때문이죠. 하지만 3중 까지는 아니더라도 2중 for 문을 이용해 List comprehension 을 작성한 코드는 종종 접하실 수 있으시니 이 기회에 익숙해 지시는 것도 좋으리라 생각 됩니다.

 

이번 예제는 이해를 돕기 위해 for문으로 구현한 코드와 list comprehension 의 기본 Syntax 와 이를 이용해 구현한 코드를 함께 비교해 보겠습니다.

# basic syntax of the nested list comprehensions
[expression for sublist in outer_list for item in sublist]

# expanded form
for sublist in outer_list:
    for item in sublist:
        expression


nested_numbers = ((1, 4, 7, 8), (2, 3, 5))
squares = [x*x for numbers in nested_numbers for x in numbers]

squares
#printed result
[1, 16, 49, 64, 4, 9, 25]

for 문의 outer 와 nested 위치를 잘 확인하시기 바랍니다.

 

6. 할당 표현식 (:=, walrus)

할당 표현식의 경우 Python 3.8 에서 새롭게 추가된 기능 입니다. 아무래도 3.8 신규 기능인지라(라고 쓰고 핑계라고 읽는...) 저도 잘 쓰지 않았던 기능이지만, 할당 표현식이 가능해 짐으로써 리스트 Comprehension 도 더욱 편리 해졌습니다.

 

예제부터 먼저 보시겠습니다.

letters = list('this is to produce a list of letters')
letters
['t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'o', ' ', 'p', 'r', 'o', 'd', 'u', 'c', 'e', ' ', 'a', ' ', 'l', 'i', 's', 't', ' ', 'o', 'f', ' ', 'l', 'e', 't', 't', 'e', 'r', 's']

import random
vowels = [letter.upper() for _ in range(0, 10) if (letter := random.choice(letters)) in list('aeoui')]

vowels
#printed result
['I', 'O', 'O', 'O', 'O']

문자 List 에서 모음(A,E,I,O,U) 만 추출해 보고 싶을 때 list comprehension 이 접근할 수 있는 letter 를 := 할당 표현식을 통해 만들고 이를 list comprehension 에서 사용할 수 있습니다.

 

7. Set Comrehension & Dict Comprehension

List comprehension 뿐만 아니라 Set Comprehension, Dict Comprehension 도 가능 합니다. 각각의 차이점을 예제를 통해 알아 볼까요?

# syntax for set comprehension
{expression for item in iterable}

numbers = (1, 34, 5, 8, 10, 12, 3, 90, 70, 70, 90)
unique_even_numbers = {number for number in numbers if number%2 == 0}

unique_even_numbers
# prineted result
{34, 70, 8, 10, 12, 90}
# syntax for dict comprehension
{key_expression : value_expression for item in iterable}

words = ('python', 'is', 'a', 'big', 'snake')
len_words = {word : len(word) for word in words}

len_words
#printed result
{'python': 6, 'is': 2, 'a': 1, 'big': 3, 'snake': 5}


len_words_p = {word : len(word) for word in words if word.startswith('p')}

len_words_p
#printed result
{'python': 6}

Set comprehension 과 Dict comprehension 은 각각 기본 Syntax 도 코드 상에 작성해 두었으니 한번 같이 확인해 보세요 :)

 

결론

List comprehension 과 더불어 Set, Dict Comprehension 까지, python 의 comprehension 은 코드의 가독성을 향상 시켜주는 좋은 기능 중 하나 입니다. 지나치지 않게, 현명하게 사용한다면 예쁘고 Pythonic 한 코드를 작성하실 수 있으실 겁니다. 화이팅!! 

반응형

'Programming' 카테고리의 다른 글

AlgoExpert 리뷰(+ 광고)  (0) 2020.03.16