FrenchDeck 예제에서 self._components를 사용한 것처럼 객체 안에 들어있는 시퀀스 속성에 위임하면 시퀀스 프로토콜을 구현하기 위한 len()과 getitem()메서드를 구현할 수 있다.
class Vector:
#중략
#...
def __len__(self):
return len(self._components)
def __getitem__(self, index):
return self._components[index]
내장된 시퀀스형은 슬라이싱 했을 때 다른 자료헝이 아니라 자신과 동일한 자료형의 객체를 생성하고 Vector를 슬라이싱해서 Vector객체를 생성하려면, 슬라이싱 연산을 배열에 위임하면 안되고 getitem() 메서드가 받은 인수를 분석해서 제대로 처리해야 한다.
#__getitem__()과 slice()의 동작 확인
class MySeq:
def __getitem__(self, index):
return index
s = MySeq()
#slice 클래스의 속성 조사
slice
dir(slice)
dir(slice)를 호출하면 indices라는 흥미로운 메서드가 보이고, help(slice.indices)명령을 실행하면 된다.
"""
S.indices(len) -> (start, stop, stride)
길이가 len인 시퀀스 S가 나타내는 확장된 슬라이스의 start와 stop인덱스 및 stride 길이를 계산하여 경계를 벗어난 인덱스는 일반적인 슬라이스를 처리하는 방법과 동일하게 잘라낸다.
"""
고로, indices는 "빠지거나 음수인 인덱스" 그리고 "대상 시퀀스보다 긴 슬라이스'를 우아하게 처리하는 내장된 시퀀스에 구현된 복잡한 논리를 보여주고, 이 메서드는 주어진 길이의 시퀀스 경계 안에 들어가도록 조정된 0이나 양수인 start, stop, stride로 구성된 정규화된 튜플을 생성한다.
len()과 getitem()이 슬라이싱을 제대로 처리하도록 구현
#vector_v2.py의 일부
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, numbers.Integral):
return self._components[index]
else:
msg = '{cls.__name__} indices must be integers'
raise TypeError(msg.format(cls=cls))
"""
getitem()의 else문에서 어떤 예외가 발생했는지 알아내기 위해서 대화형 콘솔을 이용해서 문장의 결과를 확인하고 파이썬 에선 TypeError를 발생시키며, 인덱스는 정수형이어야한다는 에러 메시지를 출력한다.
즉, 파이썬스러운 객체를 만들려면 파이썬의 객체를 흉내내야 한다.
"""
#Vector.getitem()의 테스트
v7 =Vector(range(7))