[TIL - 6 / Python] List Comprehension

haejun-kim·2020년 7월 23일
1

[Python]

목록 보기
13/19
post-thumbnail

list comprehension

list comprehension이란 새로운 리스트를 만들 때 사용할 수 있는 간단한 표현식으로 리스트와 마찬가지로 대괄호[]를 사용하여 작성한다. 그리고 만들려고 하는 원소를 표현하는 표현식으로 시작하여 for loop가 뒤에 따라오는 형식을 갖는다. For ~ if ~로 조건문을 포함한 형식도 올 수 있다.

  • [표현식 for 원소 in 반복 가능한 객체]
  • [표현식 for 원소 in 반복 가능한 객체 if문]

ex)

다음의 예시로 좀 더 이해해보자.

new_list = [ x for x in range(1,11) ]
print(new_list)
  • 실행 결과

위와 같이 list의 요소를 직접 입력해주지 않아도 쉽게 list를 만들 수 있다.
if문을 포함한 예시도 살펴보자.

odd_numbers = [ ]
for element in range(1,11):
    if (element % 2) == 1:
        odd_numbers.append(element)
        
print(odd_numbers)

위의 코드는 1~10까지를 element가 indexing하고 조건문을 사용해서 indexing한 element가 2로 나누었을 때, 나머지가 1이면, odd_numbers라는 list에 append하는 기능을 한다.

  • 실행 결과

if문을 포함한 list_comprehension으로 상단의 코드를 다음과 같이 훨씬 간단하게 구현할 수 있다.

list_comprehension = [ element for element in range(1,11) if (element % 2) == 1 ]
print(list_comprehension)
  • 실행 결과

같은 결과이지만 코드가 한줄로 훨씬 간결해졌다. 이와 같이 For loop 내부에서 새로운 리스트를 만드는 For loop는 list comprehension으로 변환할 수 있고, 반대로 list comprehension을 for loop로 변환할 수 있다.

성능

list comprehension을 사용하여 코드를 작성하는것이 list comprehension을 사용하지 않은 코드에 비해서 성능이 얼마나 뛰어난지 확인해보자.
(개발자는 새로운 지식에 대해 습득할 때 '그렇구나' 하고 넘어가는 것보단 직접 코드도 작성해보고 코드를 실행도 시켜보고 하는 습관이 중요한 것 같다.)
다음의 코드는 timeit 모듈을 사용하여 함수들을 1000번 실행한 시간을 측정하는 코드다. 이 코드를 사용하여 for loop코드 vs list comprehension코드의 동작시간을 측정하여 비교해보자.

import timeit

def for_loop():
    num_list = []
    for i in range(1000):
        num_list.append(i)

def list_comprehension():
    num_list = [ i for i in range(1000) ]

if __name__ == "__main__":
    time1 = timeit.Timer("for_loop()", "from __main__ import for_loop")
    print("for loop time = ", time1.timeit(number=1000), "milliseconds")

    time2 = timeit.Timer("list_comprehension()", "from __main__ import list_comprehension")
    print("list_comprehension time = ", time2.timeit(number=1000), "milliseconds")
  • 실행 결과

위와 같이 for loop를 사용한 코드는 0.061ms, list comprehension을 사용한 코드는 0.027ms로 더 빠르게 실행되는 것을 확인할 수 있다. 위 코드의 내용으로는 사람이 느낄정도의 성능 차이는 아니지만 코드가 복잡해지고 많은 내용을 다루는 프로그램에서 또는 0.01s처럼 아주 짧은 순간 순간이 굉장히 중요한 프로그램에서는 큰 성능 차이를 나타낼 것이다.
그리고 list comprehension으로 작성한 코드는 간결하고 데이터베이스를 조회하여 list로 만들 때 많이 사용되므로 list comprehension은 새로운 배열을 만들 때 특히 유용하다.

이렇게 성능 부분에서 좋은것은 맞지만 항상 모든 상황에서 list comprehension의 작성이 권고되지는 않는다. 예를 들어 list comprehension으로 작성된 복잡한 이중 for문의 코드는 한눈에 읽히지 않는다. 이런 경우에는 차라리 여러줄의 표현식과 조건문으로 표현하는것이 가독성면에서는 뛰어남을 보여줄 것이다.

구글의 파이썬 가이드에서 list comprehension의 장점 및 단점을 더 확인해보자.

구글 파이썬 가이드

장점

  • List, Dict, and Set comprehensions as well as generator expressions provide a concise and efficient way to create container types and iterators without resorting to the use of traditional loops, map(), filter(), or lambda.
  • Simple comprehensions can be clearer and simpler than other dict, list, or set creation techniques.
  • List, Dict, Set Comprehension은 for loop, map(), filter(), lamda를 사용하지 않고도 containeriterators를 생성하는 간결하고 효율적인 방법을 제공한다.
  • 간단한 comprehensions 다른 dict, list, set 을 사용하는것보다 간결하게 표현할 수 있다.

단점

Complicated comprehensions or generator expressions can be hard to read.

복잡한 comprehension 또는 generator 표현은 가독성이 떨어진다!


Assignments

1. 다음과 같은 도시목록의 리스트가 주어졌을때, 도시이름이 S로 시작하지 않는 도시만 리스트로 만들 때 리스트 컴프리헨션을 사용하여 함수를 작성

cities = ["Tokyo", "Shanghai", "Jakarta", "Seoul", "Guangzhou", "Beijing", "Karachi", "Shenzhen", "Delhi" ]

A-1)

아직 list comprehension 작성이 손에 익숙치 않아 list comprehension이 아닌 일반 for loop를 사용해서 어떤식으로 동작하면 되는지 구현해보았다.

cities = ["Tokyo","Shanghai","Jakarta","Seoul","Guangzhou","Beijing","Karachi","Shenzhen","Delhi"]
lists = []
for element in cities:
    temp_list = list(element)
    if temp_list[0] != "S":
        lists.append(element)

print(lists)
  • 실행 결과

먼저 주어진 list인 cities를 for를 사용해 요소로 구분지었다.이 단계에서 print해보면 도시 이름 하나씩 구분되어 출력된다. 이 구분지은 요소를 또다시 list화 시켜서 출력을 확인하면 도시이름 스펠링이 하나씩 구분되어 temp_list에 저장되어 있는것을 확인할 수 있다. 이제 대문자 "S"만 없으면 되기 때문에 S가 있는지 없는지 조건문을 사용해 비교하고 없으면 빈 리스트에 추가시켜주었다.

A-2)

위의 로직을 그대로 list comprehension으로 구현해보자.

print([ element for element in cities if "S" not in element ])
  • 실행 결과

[표현식 for 원소 in 반복 가능한 객체 if문]
위 형태에 따라서 작성해보았다.
1. [] 리스트를 만들고 안에 cities의 요소들을 뽑아 낼 반복문을 작성한다.

[ for element in cities ]
  1. 이상태에서 조건문을 추가해준다. 뽑아낸 요소에 S가 있는지 없는지 확인하기 위해 if ~ not in ~ 를 사용했다.
[ for element in cities if "S" not in element ]
  1. 반복문과 조건문을 돌고 나온 결과값인 element를 표현해준다.
[ element for element in cities if "S" not in element ]

2. 다음과 같은 도시, 인구수가 튜플의 리스트로 주어졌을때, 키가 도시, 값이 인구수인 딕셔너리를 딕셔너리 컴프리헨션을 사용한 함수를 작성

population_of_city = [
('Tokyo', 36923000),
('Shanghai', 34000000),
('Jakarta', 30000000),
('Seoul', 25514000),
('Guangzhou', 25000000),
('Beijing', 24900000),
('Karachi', 24300000),
('Shenzhen', 23300000),
('Delhi', 21753486)
]

A-1

Comprehension 없이 풀어보면 다음과 같다.

print(dict(population_of_city))

이건 comprehension을 사용하지 않았음에도 상당히 간결한 코드로 작성이 가능하다.

  • 실행 결과

A-2

Dict Comprehension은 다음과 같은 문법을 갖는다.

{Key:Value for 요소 in 입력sequence [if 조건식]}

  1. 먼저 list, tuple 형태를 어떻게 key,value로 가지고올지 생각하기 위해서 for 문으로 분해를 한 후 분해한 요소를 출력해보았다.
for element in population_of_city:
    print(element)

  1. list는 벗겨지고 튜플 형태의 데이터로 남았다. 이제 이제 인덱싱을 해서 Key, Value 값으로 설정해주면 된다.
element[0]:element[1]
  1. 위 내용을 이제 comprehension 식으로 표현해주면 다음과 같다.
print({element[0]:element[1] for element in population_of_city})
  • 실행 결과

결과는 동일하다.

0개의 댓글