프로그래밍백엔드 개발자

Python에서 'Sequence' 프로토콜을 설명하고, 이를 어떻게 구현하는지, 단순한 반복 가능한 객체와의 차이점은 무엇인지 설명하십시오.

Hintsage AI 어시스턴트로 면접 통과

답변.

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)

주요 특징:

  • 객체는 __getitem__을 통해 인덱스 접근 및 슬라이스를 지원합니다.
  • len() 기능을 지원하기 위해서는 __len__이 필요합니다.
  • __iter__가 정의되지 않은 경우 반복은 __getitem__에 기반하여 수행됩니다.

함정 질문.

내가 __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__만 정의하고 객체가 완전한 시퀀스가 되기를 기대함.
  • __getitem__을 구현했지만 slice 객체를 처리하지 않음.
  • __len__을 구현하지 않았기 때문에 len(obj)가 TypeError를 발생시킴.

실생활 예

부정적 사례

커스텀 구조체를 구현했지만, __iter__만 정의하고 슬라이스 및 인덱싱을 사용할 수 있을 것이라고 생각함.

장점:

  • for 루프에서 작동하고 제너레이터를 지원함.

단점:

  • obj[5] 또는 obj[1:3] 구문이 지원되지 않으며 오류가 발생함.
  • len(obj)도 작동하지 않음.

긍정적 사례

클래스가 슬라이스 및 int 인덱스에 대한 지원으로 __getitem__을 구현하고, __len__도 구현함.

장점:

  • 슬라이스, 인덱싱, len, 반복 등 모든 시퀀스 작업을 지원함.
  • 표준 라이브러리와의 통합(예: random.choice).

단점:

  • 슬라이스 처리를 주의 깊게 구현해야 하며, 그렇지 않으면 슬라이스 작업에서 버그가 발생할 수 있음.