Python에서 'Sequence' 프로토콜은 인덱싱과 반복할 수 있는 객체에 대한 인터페이스를 정의합니다. 예를 들어 리스트나 튜플이 이에 해당합니다. 역사적으로 시퀀스는 데이터 구조에 대한 자연스러운 작업을 지원하기 위해 Python의 초기 버전부터 존재해 왔습니다: 인덱싱, 슬라이스, 요소 반복 등의 작업을 가능하게 합니다.
문제 — 사용자 정의 클래스를 시퀀스로 만들기 위해서는 __iter__와 next 메서드만으로는 충분하지 않습니다. 시퀀스 동작을 완벽하게 지원하기 위해서는 추가적인 메서드가 필요합니다.
해결책 — User-defined 시퀀스 타입을 구현하기 위해서는 getitem 메서드(인덱싱 및 슬라이스에 필요)와 선택적으로 len 메서드(len()과 길이 확인용)를 정의해야 합니다. 이렇게 하면 객체는 반복, 인덱스 접근, 슬라이스 작업을 지원하고, Python의 시퀀스와 관련된 많은 표준 연산을 수행할 수 있습니다.
코드 예시:
class MyCounter: def __init__(self, stop): self._stop = stop def __getitem__(self, index): if 0 <= index < self._stop: return index * 10 else: raise IndexError('Out of range') def __len__(self): return self._stop c = MyCounter(5) print(c[3]) # 30 print(len(c)) # 5 for x in c: print(x)
주요 특징:
내가 __iter__와 __next__만 구현하면, 내 객체는 시퀀스(Sequence)가 될까요?
아니요. 그런 객체는 단지 반복 가능한 객체(iterable)일 뿐, 시퀀스가 되지는 않습니다. 인덱싱, 슬라이스 및 리스트 형태의 객체에 대한 표준 기능을 지원하지 않습니다.
for 루프 지원을 위해 반드시 __getitem__을 구현해야 합니까?
반드시 그럴 필요는 없습니다. __iter__가 구현되어 있으면 for 루프는 작동합니다. 그러나 __iter__가 없으면 인터프리터는 IndexError가 발생할 때까지 __getitem__을 사용하여 인덱스 0부터 시작합니다. 따라서 시퀀스의 경우 __getitem__만으로 충분합니다.
int에 대해서만 __getitem__을 구현하고, slice에 대해서는 구현하지 않아도 될까요?
기술적으로는 c[0]에 대해서는 작동하지만, c[1:4] 슬라이스를 시도하면 실패합니다. 슬라이스를 지원하기 위해서는 __getitem__이 slice 타입의 객체를 처리할 수 있어야 합니다(예: slice.indices 및 isinstance(key, slice) 참고).
코드 예시:
class S: def __getitem__(self, idx): if isinstance(idx, slice): return [x for x in range(idx.start or 0, idx.stop or 10, idx.step or 1)] return idx * 2
커스텀 구조체를 구현했지만, __iter__만 정의하고 슬라이스 및 인덱싱을 사용할 수 있을 것이라고 생각함.
장점:
단점:
클래스가 슬라이스 및 int 인덱스에 대한 지원으로 __getitem__을 구현하고, __len__도 구현함.
장점:
단점: