Python 에서 컬렉션 하나만 익히고 살아남으라면 list 야. 순서 있고, 바꿀 수 있고, 크기 자유롭고, 안에 뭐든 들어가. 다른 list 도 들어가. 대괄호로 쓰고, 비어있어도 되고, 같은 종류만 넣어도 되고, 막 섞어도 돼.
list 만드는 세 가지 방법
리터럴 (대괄호 그대로), list() 생성자, 그리고 컴프리헨션 (lesson 6 에서 다룰 거야). 90% 는 리터럴로 짤 거야.
인덱싱 — 그리고 Python 의 음수 인덱스 마법
인덱스는 0 부터 시작. 다른 언어들이랑 똑같애. Python 만의 추가 트릭: 음수 인덱스가 뒤에서부터 센다. my_list[-1] 이 마지막. my_list[-2] 가 끝에서 두 번째. 특별한 문법 따위 없어 — 음수가 그냥 *작동* 해.
슬라이싱 — for 문 세 개분의 일을 한 줄에 끝내는 문법
슬라이싱 형식은 my_list[start:stop:step]. 각 부분 다 생략 가능. my_list[:5] = 앞의 다섯. my_list[-3:] = 뒤의 셋. my_list[::-1] = 통째 뒤집기. 슬라이싱은 새 리스트 를 반환해 — 원본은 그대로. 이거 한 번 익히면 평생 for 문 수십 개를 안 써도 돼. 진짜야.
list 는 mutable — 그 대가
만든 다음에 마음대로 바꿀 수 있어. append, extend, 슬라이스 할당, del. 그게 즐거움. 대가는 — list 는 dict 키로 못 써, set 안에 못 들어가, 그리고 함수에 list 를 넘기면 그 함수가 아빠 list 를 그 자리에서 바꿀 수도 있어.
주의: 클래식 함정 — def f(x=[]). 디폴트 list 는 함수 정의 시점에 한 번만 평가되고, 모든 호출이 같은 list 를 공유해. 대신 def f(x=None): if x is None: x = [] 패턴 써.
복사 — shallow vs deep
list2 = list1 은 복사가 아니야 — 두 이름이 같은 list 를 가리킬 뿐. list2 = list1[:] 또는 list2 = list1.copy() 가 shallow 복사 — 바깥 list 는 새 거지만 안의 객체들은 여전히 공유돼. 중첩 구조 다룰 땐 copy.deepcopy() 손에 닿아.
Pythonic Way: "여러 개의 같은 종류" 가 필요하면 list. 값이 안 바뀔 거니까 tuple 쓰자 는 본능은 틀린 직관이야. tuple 의 진짜 쓸모는 lesson 3 에서.
Code
list 만드는 세 가지 방법·python
# 1. 리터럴 — 90% 는 이거
fruits = ["apple", "banana", "cherry"]
# 2. 생성자 — 어떤 iterable 도 list 로
letters = list("hello") # ['h', 'e', 'l', 'l', 'o']
range_list = list(range(5)) # [0, 1, 2, 3, 4]
# 3. 빈 list 부터 시작해서 채우기
result = []
result.append(1)
result.append(2)
print(result) # [1, 2]
음수 인덱싱 — 뒤에서부터·python
scores = [88, 92, 75, 100, 64]
print(scores[0]) # 88 첫 원소
print(scores[-1]) # 64 마지막
print(scores[-2]) # 100 끝에서 두 번째
# 범위 벗어나면 IndexError — 양/음 양쪽 다
# print(scores[5]) # IndexError
# print(scores[-6]) # IndexError
슬라이싱 — start : stop : step·python
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[2:5]) # [2, 3, 4] stop 은 EXCLUSIVE (포함 X)
print(nums[:3]) # [0, 1, 2] 처음부터
print(nums[7:]) # [7, 8, 9] 끝까지
print(nums[::2]) # [0, 2, 4, 6, 8] 두 칸씩
print(nums[::-1]) # [9, 8, ..., 0] 뒤집기
print(nums[-3:]) # [7, 8, 9] 뒤 세 개
# 슬라이싱은 항상 새 list. nums 는 그대로.
print(nums) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
슬라이스 할당 — 범위를 통째 갈아끼우기·python
data = [10, 20, 30, 40, 50]
# 3 개를 5 개로 교체
data[1:4] = ["a", "b", "c", "d", "e"]
print(data) # [10, 'a', 'b', 'c', 'd', 'e', 50]
# 슬라이스 삭제
data[1:4] = []
print(data) # [10, 'd', 'e', 50]
# 안 지우고 끼워넣기 — 길이 0 인 슬라이스
data[2:2] = ["X", "Y"]
print(data) # [10, 'd', 'X', 'Y', 'e', 50]
alias / shallow / deep — 셋의 차이·python
import copy
original = [[1, 2], [3, 4]]
alias = original # 같은 list — 두 이름이 한 객체
shallow = original[:] # 바깥은 새 거, 안은 공유
deep = copy.deepcopy(original) # 바깥도 안도 다 새 거
original[0].append(99)
print(alias) # [[1, 2, 99], [3, 4]] — original 과 공유
print(shallow) # [[1, 2, 99], [3, 4]] — 안의 list 는 공유!
print(deep) # [[1, 2], [3, 4]] — 완전 독립
mutable 디폴트 인자 함정·python
# 이렇게 하면 안 돼
def bad(item, history=[]):
history.append(item)
return history
print(bad("a")) # ['a']
print(bad("b")) # ['a', 'b'] <- history 가 재사용됨
print(bad("c")) # ['a', 'b', 'c']
# 이렇게
def good(item, history=None):
if history is None:
history = []
history.append(item)
return history
1 부터 10 까지 정수 10 개의 list 를 만들어. 그 다음 *슬라이싱만으로* (loop X, for X, comprehension X): (a) 앞 3 개 출력, (b) 뒤 3 개 출력, (c) 두 번째 원소부터 한 칸 건너 출력, (d) 뒤집어서 출력. 마지막에 원본 list 를 다시 출력해서 *그대로* 인지 확인.
Progress
Progress is local-only — sign in to sync across devices.