-
[Python] 시퀀스 프로토콜(Sequence Protocol)을 이해한다는 것IT 2026. 1. 21. 16:05

파이썬을 쓰다 보면 우리는 거의 매 순간 시퀀스를 다루고 있습니다. 리스트, 튜플 문자열, range 객체까지 모두 너무 자연스럽게 사용하다 보니, 어느 순간 이런 질문은 잘 떠오르지 않습니다.
"이 객체는 왜 인덱싱이 될까?"
"왜 for 문으로 순회할 수 있을까?"이 질문들에 대한 답을 시작하겠습니다.
우리는 이미 시퀀스를 알고 있다
다음 코드들은 너무 익숙합니다.
items = [10, 20, 30] print(items[0]) for x in items: print(x) print(len(items))이 세 가지 동작은 모두 자연스럽게 받아들여집니다. 하지만 조금만 생각해 보면 흥미로운 사실 하나가 보입니다.
- 인덱스로 접근할 수 있고, 길이를 알 수 있고, 순서대로 순회할 수 있다
파이썬은 이 세 가지 성질을 만족하는 객체를 시퀀스처럼 다룰 수 있다고 판단합니다. 이 판단 기준이 바로 시퀀스 프로토콜 입니다.
시퀀스 프로토콜이란 무엇인가
시퀀스 프로토콜은 한 문장으로 요약하면 이렇습니다.
"이 객체를 순서가 있는 데이터 묶음처럼 다뤄도 되는가?"
이를 위해 파이썬은 몇 가지 약속된 메서드를 제공합니다. 그중에서도 핵심은 다음 두 가지입니다.
- __len__()
- __getitem__()
이 두 메서드만 제대로 구현되어 있다면, 그 객체는 리스트처럼 행동하기 시작합니다.
__getitem__이 만들어내는 마법
다음과 같은 간단한 클래스를 생각해봅시다.
class MySequence: def __init__(self, data): self.data = data def __getitem__(self, index): return self.data[index]이 클래스에는 __iter__도 없고, __next__도 없습니다. 하지만 놀랍게도 다음 코드는 정상적으로 작동합니다.
seq = MySequence([1, 2, 3]) print(seq[0]) for x in seq: print(x)파이썬은 for 문을 만났을 때, 먼저 이 객체가 이터레이터인지 확인합니다. 만약 아니라면, 인덱스 0부터 시작해 __getitem__을 호출하며 값을 꺼내려 시도합니다. 그리고 더 이상 꺼낼 값이 없으면 순회를 멈춥니다.
즉, __getitem__ 하나만으로도 파이썬은 이 객체를 시퀀스로 취급할 수 있는 것입니다.
len이 있다는 것은 무엇을 의미할까
len() 함수가 동작한다는 것은 단순히 길이를 알 수 있다는 의미를 넘어섭니다.
len(seq)이 호출은 내부적으로 seq.__len__()을 실행합니다. 이는 파이썬에게 "이 객체는 크기를 개념적으로 정의할 수 있다"는 신호를 보내는 것과 같습니다.
그래서 시퀀스 프로토콜을 충실히 따르는 객체는, 단순히 순회만 가능한 객체보다 더 많은 곳에서 자연스럽게 사용될 수 있습니다.
시퀀스 프로토콜과 이터레이터의 관계
앞서 이터레이터와 제너레이터를 다뤘다면, 여기서 한 가지 연결 지점이 보입니다.
- 이터레이터 → __iter__와 __next__
- 시퀀스 → __getitem__과 __len__
이 둘은 서로 다른 프로토콜이지만, for 문이라는 동일한 인터페이스를 통해 사용됩니다. 파이썬은 객체가 어떤 프로토콜을 따르는지에 따라, 순회를 위한 전략을 다르게 선택할 뿐입니다.
이 점이 바로 파이썬의 강점입니다. 하나의 문법(for)이 여러 내부 구조를 자연스럽게 감싸고 있습니다.
슬라이싱도 프로토콜의 일부다
시퀀스를 시퀀스답게 만드는 또 하나의 요소는 슬라이싱입니다.
items[1:3]이 문법 역시 내부적으로는 __getitem__으로 처리됩니다. 인덱스 대신 slice 객체가 전달될 뿐입니다.
def __getitem__(self, index): if isinstance(index, slice): return MySequence(self.data[index]) return self.data[index]이처럼 시퀀스 프로토콜은 단순한 접근 기능을 넘어, 객체를 다루는 감각 자체를 정의합니다.
언제 시퀀스 프로토콜을 고려해야 할까?
모든 반복 가능한 객체가 시퀀스일 필요는 없습니다. 오히려 많은 경우 이터레이터나 제너레이터가 더 적절합니다.
하지만 다음과 같은 질문에 "그렇다"라고 답하게 된다면, 시퀀스 프로토콜은 좋은 선택이 됩니다.
- 이 객체는 순서가 중요한가?
- 인덱스로 접근하는 것이 자연스러운가?
- 길이라는 개념이 의미 있는가?
이 질문들은 단순히 구현의 문제가 아니라, 데이터 모델링의 문제에 가깝습니다.
마무리하며
시퀀스 프로토콜은 파이썬의 객체 모델이 얼마나 유연한지를 잘 보여주는 예입니다. 파이썬은 객체에게 "너는 리스트야"라고 강요하지 않습니다. 대신 이렇게 묻습니다.
"너는 리스트처럼 행동할 수 있니?"
그 질문에 적절한 메서드로 답할 수 있다면, 파이썬은 그 객체를 자연스럽게 시퀀스로 받아들입니다.
이 관점을 이해하고 나면, 파이썬의 많은 문법들이 단순한 기능이 아니라 약속과 신뢰 위에 세워진 구조라는 점이 보이기 시작할 것입니다.
이상입니다.
감사합니다.
'IT' 카테고리의 다른 글
[Python] __getattribute__와 getattr — 속성 접근의 마지막 퍼즐 (0) 2026.01.22 [Python] 디스크립터(descriptor)를 이해한다는 것 (0) 2026.01.22 [Python] 리스트 컴프리헨션(List Comprehension)을 이해한다는 것 (0) 2026.01.21 [Python] 이터레이터와 제너레이터, 그리고 파이썬의 지연 평가 (0) 2026.01.21 [Python] 파이썬 제너레이터(Generator)를 이해한다는 것 (0) 2026.01.21