파이썬 교육 챕터 24 리스트의 기본 개념

리스트(List)의 기본 개념: 여러 데이터를 순서대로 담는 그릇

1. 서론: 데이터의 집합, 리스트의 필요성

지금까지 우리는 숫자, 문자열, 불리언과 같은 개별적인 데이터를 다루는 방법을 배웠습니다. 하지만 실제 프로그래밍에서는 하나의 데이터뿐만 아니라, 여러 개의 데이터를 묶어서 효율적으로 관리해야 하는 경우가 훨씬 많습니다. 예를 들어, 학생들의 점수 목록, 쇼핑 카트에 담긴 상품 목록, 또는 특정 웹사이트에서 수집한 뉴스 기사 제목 목록 등이 그렇습니다. 이때 파이썬에서 여러 데이터를 순서대로 저장하고 관리하는 데 가장 널리 사용되는 자료 구조가 바로 ‘리스트(List)’입니다. 리스트는 다양한 종류의 데이터를 담을 수 있으며, 필요에 따라 요소를 추가하거나 삭제하고, 수정할 수 있는 유연성을 제공합니다. 이 챕터에서는 파이썬 리스트의 기본적인 개념과 생성 방법, 그리고 리스트가 가지는 중요한 특징들에 대해 깊이 있게 알아보겠습니다. 리스트를 마스터하는 것은 여러분의 파이썬 프로그래밍 능력을 한 단계 더 끌어올리고, 복잡한 데이터를 효율적으로 다룰 수 있는 기반을 마련하는 핵심적인 단계가 될 것입니다.

2. 리스트(List)란 무엇인가?

리스트는 여러 개의 값을 순서대로 저장하는 ‘시퀀스(Sequence)’ 자료형입니다. 파이썬에서 리스트는 대괄호([])로 묶고, 각 요소는 쉼표(,)로 구분하여 생성합니다.

2.1. 리스트의 특징

  • 순서가 있음 (Ordered): 리스트의 요소들은 순서를 가집니다. 즉, 각 요소는 고유한 인덱스(위치)를 가집니다. (0부터 시작)
  • 변경 가능 (Mutable): 리스트의 요소는 생성된 후에도 추가, 삭제, 수정이 가능합니다. 이러한 특성 때문에 리스트는 매우 유연하게 데이터를 관리할 수 있습니다.
  • 다양한 자료형 저장 가능: 하나의 리스트 안에 정수, 실수, 문자열, 불리언 등 서로 다른 자료형의 데이터를 함께 저장할 수 있습니다. 심지어 다른 리스트나 딕셔너리 같은 복합 자료형도 요소로 포함할 수 있습니다.
  • 중복 허용: 리스트는 동일한 값을 여러 번 저장할 수 있습니다.

3. 리스트 생성 방법

리스트를 생성하는 방법은 다양합니다.

3.1. 빈 리스트 생성

empty_list1 = []
empty_list2 = list()

print(empty_list1) # 출력: []
print(empty_list2) # 출력: []
print(type(empty_list1)) # 출력: <class 'list'>

3.2. 초기 값을 가진 리스트 생성

대괄호 안에 쉼표로 구분하여 요소들을 나열합니다.

# 정수형 요소들로 구성된 리스트
numbers = [1, 2, 3, 4, 5]
print(numbers) # 출력: [1, 2, 3, 4, 5]

# 문자열 요소들로 구성된 리스트
fruits = ["apple", "banana", "cherry"]
print(fruits) # 출력: ['apple', 'banana', 'cherry']

# 다양한 자료형이 섞인 리스트
mixed_list = [10, 3.14, "Hello", True]
print(mixed_list) # 출력: [10, 3.14, 'Hello', True]

# 리스트 안에 리스트 (중첩 리스트, 2차원 리스트)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix) # 출력: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 중복된 값을 가진 리스트
duplicate_numbers = [1, 2, 2, 3, 1]
print(duplicate_numbers) # 출력: [1, 2, 2, 3, 1]

3.3. list() 함수를 이용한 변환

list() 함수는 문자열, 튜플, 셋 등 다른 반복 가능한(iterable) 객체를 리스트로 변환할 때 사용합니다.

# 문자열을 리스트로 변환 (각 문자가 요소가 됨)
text = "Python"
list_from_string = list(text)
print(list_from_string) # 출력: ['P', 'y', 't', 'h', 'o', 'n']

# 튜플을 리스트로 변환
my_tuple = (10, 20, 30)
list_from_tuple = list(my_tuple)
print(list_from_tuple) # 출력: [10, 20, 30]

# 셋을 리스트로 변환 (셋은 순서가 없으므로 변환 시 순서가 바뀔 수 있음)
my_set = {1, 2, 3}
list_from_set = list(my_set)
print(list_from_set) # 출력: [1, 2, 3] (순서는 다를 수 있음)

4. 리스트 요소에 접근하기: 인덱싱(Indexing)

리스트의 각 요소는 고유한 위치 번호, 즉 ‘인덱스(Index)’를 가집니다. 인덱스를 사용하여 리스트의 특정 요소에 접근할 수 있습니다.

4.1. 양수 인덱싱 (0부터 시작)

가장 왼쪽의 요소부터 0으로 시작하여 1씩 증가하는 인덱스를 사용합니다.

fruits = ["apple", "banana", "cherry", "date"]

print(fruits[0]) # 출력: apple (첫 번째 요소)
print(fruits[1]) # 출력: banana
print(fruits[3]) # 출력: date (네 번째 요소)

# 존재하지 않는 인덱스에 접근하면 오류 발생
# print(fruits[4]) # IndexError: list index out of range

4.2. 음수 인덱싱 (-1부터 시작)

가장 오른쪽의 요소부터 -1로 시작하여 1씩 감소하는 인덱스를 사용합니다. 리스트의 마지막 요소에 접근할 때 유용합니다.

fruits = ["apple", "banana", "cherry", "date"]

print(fruits[-1]) # 출력: date (마지막 요소)
print(fruits[-2]) # 출력: cherry
print(fruits[-4]) # 출력: apple (첫 번째 요소)

4.3. 중첩 리스트 요소 접근

중첩 리스트의 요소에 접근하려면 여러 개의 인덱스를 사용합니다.

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

print(matrix[0])    # 출력: [1, 2, 3] (첫 번째 행)
print(matrix[0][0]) # 출력: 1 (첫 번째 행의 첫 번째 요소)
print(matrix[1][2]) # 출력: 6 (두 번째 행의 세 번째 요소)

5. 리스트의 길이 확인하기: len() 함수

리스트에 몇 개의 요소가 들어있는지 확인하려면 내장 함수 len()을 사용합니다.

fruits = ["apple", "banana", "cherry"]
print(len(fruits)) # 출력: 3

empty_list = []
print(len(empty_list)) # 출력: 0

6. 리스트는 변경 가능(Mutable)하다는 것의 의미

리스트의 가장 중요한 특징 중 하나는 ‘변경 가능(Mutable)’하다는 것입니다. 이는 리스트가 생성된 후에도 그 내용을 자유롭게 수정할 수 있다는 의미입니다. 반면 문자열은 ‘불변(Immutable)’ 자료형이었습니다.

6.1. 요소 값 변경

인덱스를 사용하여 특정 요소의 값을 변경할 수 있습니다.

fruits = ["apple", "banana", "cherry"]
print(f"원본 리스트: {fruits}") # 출력: 원본 리스트: ['apple', 'banana', 'cherry']

fruits[1] = "grape" # 인덱스 1의 요소를 'grape'로 변경
print(f"변경 후 리스트: {fruits}") # 출력: 변경 후 리스트: ['apple', 'grape', 'cherry']

6.2. 리스트 복사와 참조의 차이

리스트가 변경 가능하다는 특성 때문에, 리스트를 복사할 때 주의해야 합니다. 단순히 = 연산자를 사용하면 리스트의 내용이 복사되는 것이 아니라, 동일한 리스트 객체를 참조하게 됩니다.

list1 = [1, 2, 3]
list2 = list1 # list2는 list1과 동일한 객체를 참조합니다.

print(f"list1: {list1}, id: {id(list1)}")
print(f"list2: {list2}, id: {id(list2)}")

list2[0] = 100 # list2를 변경하면 list1도 함께 변경됩니다.

print(f"list1 변경 후: {list1}") # 출력: list1 변경 후: [100, 2, 3]
print(f"list2 변경 후: {list2}") # 출력: list2 변경 후: [100, 2, 3]

만약 리스트의 내용을 독립적으로 복사하고 싶다면, 슬라이싱([:])이나 list() 함수, 또는 copy() 메서드를 사용해야 합니다.

list_original = [1, 2, 3]

# 슬라이싱을 이용한 복사 (얕은 복사)
list_copy1 = list_original[:]

# list() 함수를 이용한 복사 (얕은 복사)
list_copy2 = list(list_original)

print(f"list_original: {list_original}, id: {id(list_original)}")
print(f"list_copy1: {list_copy1}, id: {id(list_copy1)}")
print(f"list_copy2: {list_copy2}, id: {id(list_copy2)}")

list_copy1[0] = 99 # list_copy1을 변경해도 list_original은 변경되지 않습니다.
print(f"list_original: {list_original}") # 출력: list_original: [1, 2, 3]
print(f"list_copy1: {list_copy1}")       # 출력: list_copy1: [99, 2, 3]

7. 결론: 데이터 컬렉션의 시작, 리스트

이 챕터를 통해 여러분은 파이썬 리스트의 기본적인 개념과 생성 방법, 그리고 리스트가 가지는 중요한 특징들에 대해 깊이 있게 학습했습니다. 리스트가 순서가 있고, 변경 가능하며, 다양한 자료형을 저장할 수 있고, 중복을 허용한다는 점을 이해했습니다. 또한, 인덱싱을 통해 리스트의 요소에 접근하는 방법과 len() 함수로 길이를 확인하는 방법, 그리고 리스트의 변경 가능성으로 인해 발생하는 복사/참조의 차이점까지 살펴보았습니다.

리스트는 파이썬 프로그래밍에서 가장 강력하고 유연하게 사용되는 자료 구조 중 하나입니다. 앞으로 여러분이 만들 대부분의 프로그램에서 데이터를 묶어서 처리해야 할 때 리스트를 사용하게 될 것입니다. 오늘 배운 리스트의 기본 개념을 바탕으로 다양한 데이터를 리스트에 담아보고, 인덱싱을 통해 요소에 접근하는 연습을 충분히 해보세요.

다음 챕터에서는 리스트의 요소를 추가, 삭제, 수정하는 등 리스트를 조작하는 다양한 메서드와 기법에 대해 자세히 알아보겠습니다. 리스트의 기본을 탄탄히 다진 여러분은 이제 데이터를 효율적으로 관리하는 진정한 프로그래머가 될 준비가 되었습니다!

파이썬 교육 챕터 24 리스트의 기본 개념