본문으로 건너뛰기
김신건의 로그

[Python] list: 가변 시퀀스

· 수정 · 📖 약 2분 · 636자/단어 #python #list #sequence #mutable #basics
python list, 파이썬 리스트, 리스트 컴프리헨션, list comprehension

정의

Python의 list는 **가변 길이 동적 배열(dynamic array)**이다. C의 ArrayList/Java ArrayList와 같은 자료구조로, 내부적으로 PyObject 포인터의 연속 배열을 over-allocation 전략으로 관리한다. 임의 인덱스 접근 O(1), 끝 추가 amortized O(1), 임의 위치 삽입·삭제 O(n).

기본 사용

empty = []
nums = [1, 2, 3]
mixed = [1, "two", 3.0, [4, 5]]   # 타입 혼합 가능
from_iter = list(range(5))         # [0, 1, 2, 3, 4]

인덱싱과 슬라이싱

python
xs = [10, 20, 30, 40, 50]
print(xs[0], xs[-1])
print(xs[1:4])        # [20, 30, 40]
print(xs[::2])        # [10, 30, 50]
print(xs[::-1])       # 뒤집기

xs[1:3] = [99, 99, 99]  # 슬라이스 할당 (길이 변경 가능)
print(xs)
결과
10 50
[20, 30, 40]
[10, 30, 50]
[50, 40, 30, 20, 10]
[10, 99, 99, 99, 40, 50]

가변 메서드

메서드동작복잡도
append(x)끝 추가amortized O(1)
extend(iter)다른 iter 합치기O(k)
insert(i, x)i번째에 삽입O(n)
pop([i])끝(또는 i) 제거 후 반환O(1) / O(n)
remove(x)첫 매치 제거O(n)
clear()비우기O(n)
reverse()제자리 뒤집기O(n)
sort(key=, reverse=)제자리 정렬 (Timsort)O(n log n)
index(x[, start, end])위치 (없으면 ValueError)O(n)
count(x)출현 횟수O(n)
copy()얕은 복사O(n)
python
xs = [3, 1, 4, 1, 5, 9, 2, 6]
xs.append(5)
xs.extend([5, 3, 5])
print(xs)

xs.sort()
print(xs)

xs.sort(reverse=True)
print(xs)

# key 정렬: 길이 기준
words = ["python", "hi", "world"]
words.sort(key=len)
print(words)
결과
[3, 1, 4, 1, 5, 9, 2, 6, 5, 5, 3, 5]
[1, 1, 2, 3, 3, 4, 5, 5, 5, 5, 6, 9]
[9, 6, 5, 5, 5, 5, 4, 3, 3, 2, 1, 1]
['hi', 'world', 'python']

sorted() vs sort()

  • list.sort(): 제자리, 반환 None
  • sorted(iter): 새 리스트 반환, 모든 iterable 가능
nums = [3, 1, 4]
nums.sort()                  # nums 자체 정렬, None 반환
new = sorted(nums)           # 새 리스트
new = sorted(nums, key=lambda x: -x)

리스트 컴프리헨션

가독성과 속도를 모두 잡는 Pythonic 패턴.

python
squares = [x ** 2 for x in range(5)]
evens = [x for x in range(10) if x % 2 == 0]
matrix = [[i * j for j in range(3)] for i in range(3)]

print(squares)
print(evens)
print(matrix)
결과
[0, 1, 4, 9, 16]
[0, 2, 4, 6, 8]
[[0, 0, 0], [0, 1, 2], [0, 2, 4]]

성능 비교: 동일 결과를 for + append로 만드는 것보다 30% 이상 빠르다(전용 바이트코드 LIST_APPEND).

얕은 복사 vs 깊은 복사

import copy
a = [[1, 2], [3, 4]]

shallow = a.copy()           # 또는 list(a), a[:]
shallow[0].append(99)        # 내부 리스트는 공유
print(a)                     # [[1, 2, 99], [3, 4]] (영향 받음)

deep = copy.deepcopy(a)
deep[0].append(100)
print(a)                     # deep만 변경됨

중요: [[]] * 3은 같은 빈 리스트 3개 참조다. 함정.

python
wrong = [[]] * 3
wrong[0].append(1)
print(wrong)         # 모두 같은 리스트

right = [[] for _ in range(3)]
right[0].append(1)
print(right)
결과
[[1], [1], [1]]
[[1], [], []]

언패킹과 스타 표현식

a, b, c = [1, 2, 3]
first, *rest = [1, 2, 3, 4]      # first=1, rest=[2, 3, 4]
first, *mid, last = [1, 2, 3, 4, 5]  # mid=[2, 3, 4]

리스트 vs 다른 타입

상황선택
변경 가능, 순서 중요list
변경 불가, 순서 중요tuple
빈번한 양끝 삽입/삭제collections.deque (양끝 O(1))
큰 수치 배열, 수학 연산numpy.ndarray
중복 없음, 멤버십 체크set

시간 복잡도 시각화

flowchart LR
    subgraph O1["O(1) / amortized O(1)"]
        A1["append(x)"]
        A2["pop()"]
        A3["xs[i] 접근"]
        A4["len(xs)"]
    end
    subgraph ON["O(n)"]
        B1["insert(i, x)"]
        B2["pop(i)"]
        B3["remove(x)"]
        B4["x in xs"]
        B5["reverse()"]
        B6["copy()"]
    end
    subgraph ONlogN["O(n log n)"]
        C1["sort()"]
        C2["sorted(xs)"]
    end

TIP

x in list 는 O(n). 자주 확인한다면 set 으로 변환 후 x in myset (O(1)) 이 훨씬 빠르다.

함수형 패턴

map, filter, functools.reduce 는 list 에서 자주 쓰인다.

python
from functools import reduce

nums = [1, 2, 3, 4, 5]

doubled = list(map(lambda x: x * 2, nums))
evens   = list(filter(lambda x: x % 2 == 0, nums))
total   = reduce(lambda acc, x: acc + x, nums, 0)

print(doubled)
print(evens)
print(total)
결과
[2, 4, 6, 8, 10]
[2, 4]
15

NOTE

map / filter 는 lazy iterator 를 반환한다. 결과를 즉시 쓰려면 list(...) 로 감싼다. 컴프리헨션이 더 Pythonic 하지만 기존 코드에서 자주 보인다.

zip 과 enumerate

names = ["Alice", "Bob", "Carol"]
scores = [90, 85, 92]

# zip: 두 리스트를 쌍으로
for name, score in zip(names, scores):
    print(f"{name}: {score}")

# enumerate: 인덱스와 값
for i, name in enumerate(names, start=1):
    print(f"{i}. {name}")

# transpose: zip(*matrix)
matrix = [[1, 2, 3], [4, 5, 6]]
transposed = list(zip(*matrix))  # [(1, 4), (2, 5), (3, 6)]

성능 함정

  • list.insert(0, x): O(n). 큐로 쓰면 deque 사용
  • x in list: O(n). 멤버십 체크가 빈번하면 set
  • 큰 정수 배열: numpy.ndarray가 메모리 1/4, 속도 10배+
  • list + list 연결: O(n+m). 여러 번 반복하면 itertools.chain

list 내부 구조 (CPython)

CPython 의 listobject.c 구조:

PyListObject {
    ob_refcnt, ob_type       // GC 헤더
    ob_item  →  [PyObject*, PyObject*, ...] (포인터 배열)
    ob_size    // 현재 원소 수
    allocated  // 할당된 슬롯 수 (>= ob_size)
}

appendallocated == ob_size 이면 over-allocation:

새 크기 = (ob_size + 1) * 9 / 8 + 6

이 공식으로 연속 append 가 amortized O(1).

관련 위키

이 글의 용어 (6개)
[Python] collections: Counter, deque, defaultdict, OrderedDict, ChainMappython
정의 모듈은 dict/list/tuple/set의 특수화 컨테이너를 제공한다. 표준 라이브러리에 있는 데이터 구조 도구함의 핵심. | 클래스 | 용도 | |--------|---…
[Python] Comprehension: list, dict, set, generatorpython
정의 Comprehension(컴프리헨션)은 iterable로부터 list/set/dict/generator를 선언적·간결하게 만드는 문법이다. 일반 + 보다 30-50% 빠르며…
[Python] functools: cache, partial, reduce, singledispatchpython
정의 는 함수형 프로그래밍 도구와 함수 변환 데코레이터를 제공하는 표준 라이브러리. 메모이제이션, 부분 적용, 디스패치, 데코레이터 메타데이터 등을 다룬다. lrucache / …
[Python] Iterator, Generator, yieldpython
Iterator Protocol Iterator는 와 를 구현한 객체. Iterable은 만 가지면 된다 (iterator를 반환). 문은 내부적으로 로 iterator를 얻고 …
[Python] itertools: 효율적인 iterator 빌딩 블록python
정의 는 메모리 효율적인 iterator 조합기를 제공한다. lazy 평가로 무한 시퀀스도 다루며, C로 구현되어 빠르다. Haskell/Lisp 영향을 받은 함수형 도구함. 무…
[Python] tuple: 불변 시퀀스와 NamedTuplepython
정의 은 불변(immutable) 시퀀스다. 와 거의 같은 API를 가지나 생성 후 변경 불가하며, 그 덕에 dict 키나 set 원소로 사용 가능하다(해시 가능). 함수의 다중…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기