파이썬 강좌 – 지능형 리스트(List Comprehension) – 리스트 갖고 놀기

  1. 프롤로그
  2. 개발 첫걸음
    1. 컴퓨터 구성요소 – 컴퓨터는 어떤 걸 할 수 있나?
    2. 개발과 관련된 용어
    3. 파이썬의 선택 – 왜 파이썬인가?
    4. 파이썬 설치 – Hello World 출력하기
    5. Visual Studio Code 의 편리한 기능
    6. REPL과 콘솔 창 – 파이썬 동작시키기
  3. 파이썬 기초
    1. 기초 입출력 – 소통하기
    2. 변수와 대입 – 기억하기
    3. 연산자 – 계산하기
    4. 조건문 – 분기를 만들기
    5. 반복문 – 비슷한 작업을 반복하기
    6. 반복문 코딩하기
    7. 변수와 리스트 – 비슷한 변수들을 묶기
    8. for, range – 리스트의 항목을 다루기
    9. 함수와 메소드의 호출 – 편리한 기능 이용하기
    10. 모듈 설치와 사용 – 유용한 기능 끌어다 쓰기
    11. 문자열 – 텍스트 다루기
  4. 파이썬 중급
    1. 함수를 직접 만들기 – 자주 쓰는 기능을 묶기
    2. 딕셔너리, 튜플, 세트 – 변수를 다양한 방법으로 묶기
    3. 클래스와 객체 – 변수를 사람으로 진화시키기
    1. 상속 – 클래스를 확장하기
    2. 정체성과 동질성 – 객체의 성질
    3. 특별 메소드와 연산자 – 파이썬의 내부 작동방식 이해하기
    4. 다양한 함수 인수 – 유연한 함수 만들기
    5. 슬라이싱 – 리스트 간편하게 접근하기
    6. 지능형 리스트(List Comprehension) – 리스트 갖고 놀기
    7. namedtuple - 데이터 묶음 손쉽게 만들기
    8. 조건 표현식 (Conditional Expression) - 간단한 분기 나타내기
    9. 코드 스타일 - 코드의 일관성 유지하기
    10. 명령문, 표현식 – 문법을 이루는 것들
    11. 본격적인 검색 해보기
  5. 파이썬 고급
    1. 일급 함수 다루기
    2. NotImplementedError와 NotImplemented
    3. 어노테이션 – 수월하게 프로그래밍하기
    1. 내장 함수 톺아보기
    2. 예외와 에러 – 예상치 못한 상황에 대응하기 (v0.1)
    3. 변수의 범위 – 이름 검색의 범위
  6. 파이썬 심화
    1. 시퀀스와 반복자 – 반복과 순회를 자유자재로 다루기
    2. 데코레이터 – 함수의 기능을 강화하기
    3. 프로퍼티
    4. 제너레이터
    5. async와 await
    6. 객체로서의 클래스 – 클래스를 동적으로 정의하기
  7. 파이썬 프로젝트 실습
    1. 원카드 게임 만들기 (1)
    2. 원카드 게임 만들기 (2)
    3. 원카드 게임 만들기 (3) (작성중)
    4. 턴제 자동 전투 게임 만들기 (작성중)
  8. 실전 (파이썬 외적인 것들)
    1. 정규표현식 – 문자열을 검색하고 치환하기 (작성중)
    2. 유니코드 – 컴퓨터에서 문자를 표기하는 방법
    3. html, css, 인터넷 – 자동화 첫 걸음 내딛기
    4. 네트워크 – 인터넷으로 통신하는 방법
    5. 문서 – 문맥을 읽어보기

지능형 리스트 또는 리스트 컴프리헨션이란, 좀 더 편리하게 리스트를 만들 수 있는 방법을 제공합니다.

List Comprehension을 한국어로 자연스럽게 번역한 것이 지능형 리스트이고, 우리나라의 저명한 파이썬 서적에 저렇게 표기되어 있습니다. 일각에서는 리스트 표현식이라는 표현도 심심찮게 보입니다. 다만 한국어로 번역된 파이썬 공식 문서에는 그대로 음차한 리스트 컴프리헨션이라고 되어있어, 추후 구글 검색시 유의해야 합니다.


간단한 예시

어떤 수의 제곱수를 담고 있는 리스트를 만들고 싶다고 가정합시다. 즉 차례대로 1, 4, 9, 16, 25, 36 .. 이런 식으로 이어지는 리스트입니다. 1의 제곱부터 20의 제곱까지 담겨 있는 리스트를 만드려면 아래와 같이 하면 됩니다.

ls = []
for i in range(1, 21):
    ls.append(i*i)
print(ls)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400]

코드를 이해하기에는 그렇게 많이 어렵지 않습니다.


하지만 지능형 리스트를 이용하면 리스트를 만드는 과정을 단 한 줄로 줄일 수 있습니다.

ls = [i*i for i in range(1,21)]
print(ls)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400]

짜잔! 훨씬 읽기가 편해졌습니다. 이게 어떻게 가능하냐구요? 어떻게 for문이 리스트 대괄호 안에 들어가있을 수 있는 것일까요? 왜냐하면 파이썬이 “지능형 리스트”라는 이름으로 동작할 수 있도록 해놓았기 때문입니다!


for-in 루프 용법

기본

가장 기본적인 형태의 용법은 다음과 같습니다.

[ A for B in C ]

  • A: 최종적으로 리스트에 들어갈 요소입니다. 함수 호출이 되어도 되고 계산 식이 되어도 됩니다. 여기서는 B를 활용할 수 있습니다. (대부분 활용합니다)
  • B : C의 요소 하나하나를 나타냅니다.
  • C : 지능형 리스트에서 활용할 또 다른 리스트입니다.

가장 간단한 형태의 예시를 살펴보면 다음과 같습니다. 우선 위 예제와 똑같이 제곱 수들을 구하는 예제입니다. 아래 예제는 * 연산을 시도하고 있습니다.

ls = [i*i for i in [-5, -4, -3, 1, 2, 3]]
[25, 16, 9, 1, 4, 9]

함수 호출도 가능합니다.

ls = [abs(i) for i in [-5, -4, -3, 1, 2, 3]]
[5, 4, 3, 1, 2, 3]

좀 더 복잡하게 하면 다음처럼도 해볼 수 있겠지요.

people = [{'name': '철수', 'age': 8},
          {'name': '영희', 'age': 12},
          {'name': '민수', 'age': 9}]
ls = [f'{person["name"]}은 {person["age"]}살입니다.' for person in people]
print(ls)
['철수은 8살입니다.', '영희은 12살입니다.', '민수은 9살입니다.']

중첩

지능형 리스트는 내부에서 중첩할 수 있습니다. 그러니까 for-in 부분을 여러 번 사용할 수 있다는 뜻입니다. 간단하게 두 개의 for문을 돌려 하나의 튜플을 만드는 걸 상상해봅시다. 아래 예제에서는 두 개의 for-in만 중첩되었지만 중첩 횟수는 제한이 없습니다!

a = [(i, j) for i in range(3) for j in range(4)]
print(a)
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]

이 때에는 순서가 어떻게 되냐면, 일단 i를 0~2까지 돌립니다. 그 내부에서 j를 0~3까지 돌립니다. 그 이후 ij가 포함된 튜플을 생성합니다. 이를 일반 식으로 고치면 다음과 같습니다.

a = []
for i in range(3):
    for j in range(4):
        a.append((i, j))
print(a)
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]

중첩할 때, 두 개의 지능형 리스트를 중첩시킬 수도 있습니다.

a = [[i for i in range(3)] for j in range(4)]
print(a)
[[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]

사실 중첩 때부터 헷갈리기 시작하는데, 식들의 평가 순서는 다음과 같습니다.

  1. [A(2) for-in(1)]
  2. [A(3) for-in(1) for-in(2)]
  3. [[A(3) for-in(2)] for-in(1)] (1번과 사실 같은 형태입니다.)

이 때 후번에 있는 요소는 전번에서 생긴 변수를 사용할 수 있습니다. (1)에서 생긴, for 루프의 각 요소를 대변하는 변수를 A에서 사용할 수 있는 것처럼요.


if 활용

우리가 만약에 어떤 리스트에 있는 값 중 특정 값 이상만 받아오고 싶다면 어떻게 해야 할까요? 아래는 사람이 딕셔너리 형태로 저장되어 있는 리스트에서 age 키의 값이 9 이상인 항목을 가져오고 싶다고 가정합니다. 즉 9살 이상인 사람만 받고 나머지는 걸러내겠다는 뜻이지요.

people = [{'name': '철수', 'age': 8},
          {'name': '영희', 'age': 12},
          {'name': '민수', 'age': 9}]
ls = [person for person in people if person["age"] >= 9]
print(ls)
[{'name': '영희', 'age': 12}, {'name': '민수', 'age': 9}]

바로 for 뒤에 if 절을 추가시켜주면 됩니다. 이렇게 하면 해당 if 절의 결과가 참일 때에만 루프가 실행됩니다.


복잡한 활용

중첩과 if를 섞어서 여러 번 많이많이 사용할 수 있습니다. 보기만 해도 복잡해지는 다음 지능형 리스트를 확인해봅시다.

a = [(i, j) for i in [2, 3, 4, 5, 7, 8] if i % 2 == 0 for j in range(i - 2, i + 2) if i * j <= 50]
print(a)
[(2, 0), (2, 1), (2, 2), (2, 3), (4, 2), (4, 3), (4, 4), (4, 5), (8, 6)]

사실 이렇게까지는 안 합니다. 왜냐하면 가독성 좋으려고 하려는 게 되려 가독성을 헤치기 때문입니다. 가독성이 좋지 않다면 원래 쓰던 대로 for-in 반복문과 if 조건문을 분리해서 사용합시다. 뭐, 예제에 불과한 위 코드를 굳이 해석하자고 하면, “[2, 3, 4, 5, 7, 8]이라는 리스트 중 짝수인 i를 순회한다. 그리고 i - 2부터 i + 1까지의 중 i와 곱하여 50 이하인 j를 뽑아낸다. 그리고 (i, j) 튜플을 만든다.” 입니다. 위의 식을 한번 코드로 재구성해봅시다.

a = [(i, j) for i in [2, 3, 4, 5, 7, 8] if i % 2 == 0 for j in range(i - 2, i + 2) if i * j <= 50]

# 이를 반복문과 조건문으로 고치면 다음과 같습니다.

a = []
for i in [2, 3, 4, 5, 7, 8]:
    if i % 2 == 0:
        for j in range(i - 2, i + 2):
            if i * j <= 50:
                a.append((i, j))
 

이제 읽을 수는 있는 수준이지요? 굳이 지능형 리스트를 고수하겠다 하면, 지능형 리스트도 중간에 개행은 가능하니까 다음처럼 그나마 보기 편하게는 만들어볼 수 있겠습니다.

a = [(i, j)
     for i in [2, 3, 4, 5, 7, 8]
     if i % 2 == 0
     for j in range(i - 2, i + 2)
     if i * j <= 50]
print(a)

else를 넣고 싶다면?

지능형 리스트에서는 대괄호([])의 내부에 for-in 문과 if 까지만 들어갈 수 있고 else와 같은 다른 것들은 들어가지 못합니다. 그래서 if 조건에 충족하지 못할 시에 다른 값을 넣고 싶은 경우에는 제일 최종적으로 계산되는 곳에 조건 표현식 (conditional expression)을 이용하도록 합시다.

a = ["짝" if i % 2 == 0 else "홀" for i in range(10)]
print(a)
['짝', '홀', '짝', '홀', '짝', '홀', '짝', '홀', '짝', '홀']

연습 문제

  1. 지능형 리스트는 언제 사용하는가?

프로그래밍 문제

  1. 지능형 리스트를 이용하여, 구구단의 곱셈하려는 숫자와 그 결과를 모두 튜플에 넣어서 리스트로 만드세요. 다음과 같이 출력되어야 합니다.
    [(2, 1, 2), (2, 2, 4), (2, 3, 6), (2, 4, 8), ... , (9, 8, 72), (9, 9, 81)]

프로그래밍 문제 정답

  1. 코드입니다.
a = [(i, j, i*j) for i in range(2, 10) for j in range(1, 10)]
print(a)
  1. 프롤로그
  2. 개발 첫걸음
    1. 컴퓨터 구성요소 – 컴퓨터는 어떤 걸 할 수 있나?
    2. 개발과 관련된 용어
    3. 파이썬의 선택 – 왜 파이썬인가?
    4. 파이썬 설치 – Hello World 출력하기
    5. Visual Studio Code 의 편리한 기능
    6. REPL과 콘솔 창 – 파이썬 동작시키기
  3. 파이썬 기초
    1. 기초 입출력 – 소통하기
    2. 변수와 대입 – 기억하기
    3. 연산자 – 계산하기
    4. 조건문 – 분기를 만들기
    5. 반복문 – 비슷한 작업을 반복하기
    6. 반복문 코딩하기
    7. 변수와 리스트 – 비슷한 변수들을 묶기
    8. for, range – 리스트의 항목을 다루기
    9. 함수와 메소드의 호출 – 편리한 기능 이용하기
    10. 모듈 설치와 사용 – 유용한 기능 끌어다 쓰기
    11. 문자열 – 텍스트 다루기
  4. 파이썬 중급
    1. 함수를 직접 만들기 – 자주 쓰는 기능을 묶기
    2. 딕셔너리, 튜플, 세트 – 변수를 다양한 방법으로 묶기
    3. 클래스와 객체 – 변수를 사람으로 진화시키기
    1. 상속 – 클래스를 확장하기
    2. 정체성과 동질성 – 객체의 성질
    3. 특별 메소드와 연산자 – 파이썬의 내부 작동방식 이해하기
    4. 다양한 함수 인수 – 유연한 함수 만들기
    5. 슬라이싱 – 리스트 간편하게 접근하기
    6. 지능형 리스트(List Comprehension) – 리스트 갖고 놀기
    7. namedtuple - 데이터 묶음 손쉽게 만들기
    8. 조건 표현식 (Conditional Expression) - 간단한 분기 나타내기
    9. 코드 스타일 - 코드의 일관성 유지하기
    10. 명령문, 표현식 – 문법을 이루는 것들
    11. 본격적인 검색 해보기
  5. 파이썬 고급
    1. 일급 함수 다루기
    2. NotImplementedError와 NotImplemented
    3. 어노테이션 – 수월하게 프로그래밍하기
    1. 내장 함수 톺아보기
    2. 예외와 에러 – 예상치 못한 상황에 대응하기 (v0.1)
    3. 변수의 범위 – 이름 검색의 범위
  6. 파이썬 심화
    1. 시퀀스와 반복자 – 반복과 순회를 자유자재로 다루기
    2. 데코레이터 – 함수의 기능을 강화하기
    3. 프로퍼티
    4. 제너레이터
    5. async와 await
    6. 객체로서의 클래스 – 클래스를 동적으로 정의하기
  7. 파이썬 프로젝트 실습
    1. 원카드 게임 만들기 (1)
    2. 원카드 게임 만들기 (2)
    3. 원카드 게임 만들기 (3) (작성중)
    4. 턴제 자동 전투 게임 만들기 (작성중)
  8. 실전 (파이썬 외적인 것들)
    1. 정규표현식 – 문자열을 검색하고 치환하기 (작성중)
    2. 유니코드 – 컴퓨터에서 문자를 표기하는 방법
    3. html, css, 인터넷 – 자동화 첫 걸음 내딛기
    4. 네트워크 – 인터넷으로 통신하는 방법
    5. 문서 – 문맥을 읽어보기

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Scroll to top