프로그래밍/Python

<22.02.28> Python 다시 배우기 #02. 리스트 컴프리헨션

mayberry 2022. 2. 28. 15:11

 

Python 공부를 하다보면, 가끔 이런 글을 볼 수 있다. '파이썬스러운 코드를 작성하라' 파이썬스러운 코드란 무엇일까. 그것을 또 검색하다 보면 파이썬에서만 사용할 수 있는 문법구조를 활용하여 작성하는 것이라고 나와있는데, 그 중 대표적인 것이 '리스트 컴프리헨션'이다. 오늘은 리스트 컴프리헨션에 대해 알아보고자 한다.

 

comprehensive의 의미

comprehensive는 '포괄적인', '종합적인'이라는 뜻을 가지고 있다. 이는 오늘 포스팅에서 다룰 리스트 컴프리헨션을 잘 나타내는 어휘이다. Python에서 for문을 작성할 때 여러 줄에 걸쳐 작성하는 것을 단 한 줄로 표현할 수 있는 강력한 기능이다. 하지만 for문이 길어질 것 같으면 그냥 여러줄에 걸쳐 쓰는 것이 좋다. 코드는 다른 사람이 보기에도 이해할 수 있어야 하니까.

 

일반적인 for문

위 테스트 코드는 0부터 29까지의 정수를 test라는 리스트에 추가하는 코드이다. test라는 리스트에 30개의 수를 순차적으로 추가하는 코드인데 결과물은 [1,2,3,4,5 ... 27,28,29]가 된다. 하지만 리스트 컴프리헨션을 사용하면 아래와 같이 나타낼 수 있다.

 

리스트 컴프리헨션 사용

3줄에 걸쳐 작성되던 for문이 단 한줄로 표현할 수 있게 되었다. 이처럼 대괄호만을 활용하여 직관적으로 리스트를 생성할 수 있는 이러한 표기법을 '리스트 컴프리헨션(List Comprehension)'이라고 한다. 

 

간단한 리스트를 만드는데는 위와 같은 방식으로 만들 수도 있다. 하지만 위와 같은 경우라면 차라리 아래와 같은 코드를 짜는 것이 더욱 효율적일지도 모른다.

 

range 함수를 활용하여 리스트 만들기

range 함수에 정수를 넣으면 0부터 29까지 이터러블 객체를 반환하는데 이 때 list()로 묶어주어 [1,2,3,4,5 ... 27, 28, 29]와 같은 형태의 리스트를 만들 수 있다. 리스트 컴프리헨션이 빛을 보는 경우는 if와 else문을 사용하거나 간단한 이중 for문을 사용할때라고 생각한다.

 

1. 리스트 컴프리헨션 + if문

 

리스트 컴프리헨션에 if 문을 활용하여 사용자가 조건에 맞는 리스트를 생성할 수 있다. 아래의 테스트 코드를 보자.

 

일반적인 for문

위의 코드는 0에서 29까지의 숫자 중 2의 배수만을 test 리스트에 추가하는 코드이다. for문을 실행하고 i가 갱신 될 때마다 i에 2를 나누어 0이 되는지를 확인한다. 아래는 위 코드를 리스트 컴프리헨션으로 표현한 것이다.

 

리스트 컴프리헨션

가장 맨 처음 소개했던 리스트 컴프리헨션 코드문과 비교하면, 기존의 코드의 뒤에 if 문이 추가되었음을 알 수 있다. if문을 하나만 작성하고 싶다면 위와 같이 for문 뒤에 곧바로 if 문을 입력해주면 된다. 그러면 if ~ else문을 사용하고 싶을 때는?

 

그럴 때는 if문을 왼쪽에 작성하면 된다. 왼쪽이라 함은, 리스트 컴프리헨션문에서 변수 바로 옆을 의미한다. 바로 위의 코드에서 else문을 추가하여 작성해보겠다. 어떤 코드를 작성할 것이냐면, 0에서 29까지의 숫자 중 2의 배수는 그대로 저장하고, 그렇지 않은 수는 -1로 표기하는 리스트를 작성할 것이다.

 

리스트 컴프리헨션 + else문

else문을 포함한 if문을 사용하기 위해서는 위와 같이 코드를 작성해야한다. else문을 사용하고 싶다고 해서 아래와 같이 작성하면 에러가 발생한다.

 

Syntax Error

else문을 사용하고 싶다면 for문의 좌측에 작성해야 한다. elif문은 사용할 수 없을까? if ~ else도 되는데... 리스트 컴프리헨션 내에서는 elif를 사용할 수 없지만 if ~ else문을 여러번 사용함으로써 elif를 작성하는 것과 비슷한 효과를 줄 수 있다. 아래의 테스트 코드를 보자.

 

if ~ else문이 두 번 들어갔다. 우선 위 테스트 코드를 설명하자면, 0에서 29까지의 숫자 중 2의 배수와 3의 배수는 i(숫자)를 출력하지만, 그렇지 않은 경우 -1을 출력하는 리스트를 반환하는 코드이다.ㅡ리스트 컴프리헨션을 위와 같이 작성하면 가독성이 떨어진다. 개인적으로는 저렇게 쓸 바엔 여러줄에 걸쳐 쓰는 것이 낫다고 생각한다.ㅡ if~else문이 총 두 번 사용되었는데 이를 분석하면 아래와 같다.

 

1) i if i % 2 == 0 

 

i가 2의 배수일때는 i를 출력.

 

2) else i if i % 3 == 0

 

i가 2의 배수가 아니고 3의 배수일때도 i를 출력.

 

3) else -1

 

1번 2번 조건에도 맞지 않을 경우 -1을 출력

 

사실 if else문이 여러번 사용되어야 할 for문이라면 그냥 풀어서 사용하는 것이 나을 것 같다.

 

2. 리스트 컴프리헨션 + 연산자 사용

 

리스트 컴프리헨션을 통해 값을 순차적으로 리스트에 포함시킬 때, 특정한 연산을 사용해야 한다면 for문 앞에 연산자를 사용해줄 수 있다. 아래처럼 말이다.

 

 위와 같이 작성하면 test라는 리스트에는 i에 3을 곱한 값들이 리스트에 저장된다. 더하기를 해주고 싶다면 i + 3을, 빼기를 하고 싶다면 i - 3을 해주면 된다. 나는 예전에 리스트 컴프리헨션을 처음 배울 때 +=이나 *=과 같은 연산자를 사용해야 i에 대한 연산이 적용되는 줄 알고 아래와 같이 작성했는데 아니었다.

 

3. 리스트 컴프리헨션 + 함수 사용

 

리스트 컴프리헨션을 작성하면서 사용자가 설정한 임의의 함수를 적용해야하는 경우가 생길 때, 역시 리스트 컴프리헨션을 사용할 수 있다. 

 

plus라는 함수는 입력으로 받은 정수에 10을 더해 반환하는 간단한 함수다. 4번째 Line에서는 리스트 컴프리헨션을 통해 0에서 29까지의 각 정수에 plus 함수를 적용한 것이다. 결과물은 아래와 같다.

 

 

4. 리스트 컴프리헨션으로 이중 for문 작성하기

 

리스트 컴프리헨션으로 이중 for문을 작성하는 것도 가능하다. 이를 통해 2차원 리스트나 배열을 생성할 수 있다. 아래는 이중 for문을 작성한 테스트 코드이다.

 

10차원 리스트를 만드는 코드이다. 이중 for문을 활용하여 하나의 리스트안에 10개의 하위 리스트들을 삽입한 10차원 리스트이다. 리스트 컴프리헨션은 위와 같은 이중 for문도 한 줄로 작성할 수 있게 도와준다.

 

리스트 컴프리헨션을 활용한 이중 for문

일반적인 for문과 비교했을 때, 상위 for문은 리스트 컴프리헨션의 뒷 쪽에 위치하고, 하위 for문은 리스트 컴프리헨션의 앞쪽에 위치한다. 이 때 전체 리스트에 추가되는 하위 리스트에는 i개 만큼의 원소값들이 추가된다. 하위 리스트의 크기는 모두 동일하지 않고, 상위 for문의 i값에 따라 하위 리스트의 길이가 변한다. test2를 출력하면 아래와 같다.

위 테스트 코드에서는 중첩 리스트를 생성하는 것이여서 리스트 컴프리헨션을 중첩으로 사용했다. 이번엔 리스트 컴프리헨션의 중첩 사용 없이 for 문만을 활용하여 작성해보겠다.

 

i의 범위는 0에서 10, j의 범위는 10에서 20까지 설정하여 i와 j라는 변수가 쌍으로 묶여 튜플의 형태로 리스트에 저장된다. 이렇게 되면 튜플의 첫 번째 자리에는 0에서 9까지의 정수들이, 두 번째 자리에는 10에서 19까지의 정수들이 위치할 수 있게 된다. 결국 위 코드는 '0에서 9까지의 정수'와 '10에서 19'까지의 정수'로 조합할 수 있는 모든 경우의 수를 포함하는 리스트를 출력한다. 위 코드를 풀어쓰면 아래와 같다.

 

컴프리헨션을 사용하여 작성한 코드와의 차이점이 보이는가? 컴프리헨션의 첫 번째 for문이 상위 for문이 되고, 컴프리헨션의 두 번째 for문이 하위 for문이 된다. 

 

5. 그 외

 

컴프리헨션 구문은 리스트를 작성할 때만 사용할 수 있는 것이 아니다. key와 value를 가지는 딕셔너리(사전형) 자료구조에도 사용할 수 있다. 정해진 범위가 있다면 enumerate 함수를 활용하여 아래와 같은 코드를 작성하여 딕셔너리를 만들 수 있다.

 

위 코드는 여태 소개했던 형태와는 다르게 {}로 감싸져있다. Python의 딕셔너리 자료구조눈 {}의 형태로 이루어져 있기 때문에 딕셔너리 자료구조를 구성하고 싶다면 위와 같이 사용할 수 있다. 여기서 enumerate 함수는 시퀀스를 입력으로 받아 개별 원소값마다 순차적인 값을 부여해준다. v에는 숫자가, k는 데이터ㅡ여기서는 'A','B' ... 와 같은 개별 문자열들ㅡ가 들어간다. 그리고 이를 k:v로 바꾸어 key를 알파벳으로, value를 숫자로 지정할 수 있다. 확인해보면 아래와 같이 나온다.

 

enumerate는 예시처럼 직접 입력한 문자열 이외에도 튜플이나 리스트와 같은 이터러블 객체들이 들어갈 수 있다.

 

 

 

 

오늘은 컴프리헨션 구문에 대해 포스팅하였다. 알고리즘 문제를 풀다보면 간단한 for문이나 if문을 섞은 for문을 작성할 때 컴프리헨션을 사용하는 경우가 있다. 남발하여 사용하면 가독성이 떨어지는 코드가 되니, 적재적소에 사용하는 것이 현명하다.

 

P. S 조잡한 글 읽어주셔서 감사합니다! 혹시나 틀린 정보가 있다면 과감한 지적(하지만 욕은 삼가해주시면 감사하겠습니다 ㅎㅎ) 부탁드립니다.  

'프로그래밍 > Python' 카테고리의 다른 글

<22.02.24> Python 다시 배우기 #01. 문자열 함수  (0) 2022.02.24