class FrequencyList(list):
def __init__(self, members):
super().__init__(members)
def frequency(self):
counts = {}
for item in self:
counts[item] = counts.get(item, 0) + 1
return counts
foo = FrequencyList(['a', 'b', 'a', 'c', 'b', 'a', 'd'])
print('길이: ', len(foo))
foo.pop()#리스트 요서 끄집어 내기
print('pop한 다음:', repr(foo))
print('빈도:', foo.frequency())
길이: 7
pop한 다음: ['a', 'b', 'a', 'c', 'b', 'a']
빈도: {'a': 3, 'b': 2, 'c': 1}
class BinaryNode:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
bar = [1, 2, 3]
bar[0]
1
다음 특별 메서드로 해석된다.
bar.__getitem__(0)
1
BinaryNode 클래스가 시퀀스처럼 작동하게 하려면 트리 노드를 깊이 우선 순회하는 커스텀 getitem메서드 구현을 제공하면 된다.
class IndexableNode(BinaryNode):
def _traverse(self):
if self.left is not None:
yield from self.left._traverse()
yield self
if self.right is not None:
yield from self.right._traverse()
def __getitem__(self, index):
for i, item in enumerate(self._traverse()):
if i == index:
return item.value
raise IndexError(f'인덱스 범위 초과: {index}')
이진 트리는 늘 만들던 대로 만들면 된다.
tree = IndexableNode(10,
left=IndexableNode(5,left=IndexableNode(2),right=IndexableNode(6,right=IndexableNode(7))),
right=IndexableNode(15,left=IndexableNode(11)))
이 트리를 left나 right 애트리뷰트를 사용해 순회할 수도 있지만 추가로 리스트 처럼 접근할 수 있다.
print('LRR:', tree.left.right.right.value)
print('인덱스 0:', tree[0])
print('인덱스 1:', tree[1])
print('11이 트리 안에 있나?', 11 in tree)
print('17이 트리 안에 있나?', 17 in tree)
print('트리:', list(tree))
LRR: 7
인덱스 0: 2
인덱스 1: 5
11이 트리 안에 있나? True
17이 트리 안에 있나? False
트리: [2, 5, 6, 7, 10, 11, 15]
문제는 getitem 을 구현하는 것만으로는 리스트 인스턴스에서 기대할 수 있는 모든 시퀀스 의미 구조를 제공할 수 없다는 데 있다.
len(tree)
TypeError Traceback (most recent call last)
in
----> 1 len(tree)
TypeError: object of type 'IndexableNode' has no len()
class SequenceNode(IndexableNode):
def __len__(self):
for count, _ in enumerate(self._traverse(), 1):
pass
return count
tree = SequenceNode(
10,
left=SequenceNode(
5,
left=SequenceNode(2),
right=SequenceNode(
6,
right=SequenceNode(7))),
right=SequenceNode(
15,
left=SequenceNode(11))
)
print('트리 길이:', len(tree))
트리 길이: 7
어떤 클래스가 올바른 시퀀스가 되려면 두 메서드(getitem과 len)을 구현하는 것만으로는 충분하지 않다.
현재까지 만든 트리 클래스에는 파이썬 프로그래머가 시퀀스에 있을 것으로 예상하는 count나 index 메서드도 들어 있지 않다.
자신만의 컨테이너 타입을 직접 정의하는 것은 생각보다 훨씬 더 어려운 일임을 알 수 있다.
collections.abc 모듈 안에는 컨테이너 타입에 정의해야 하는 전형적인 메서드를 모두 제공하는 추상 기반 클래스 정의가 여러가지 들어있다.
이런 추상기반 클래스의 하위 클래스를 만들고 필요한 메서드를 구현을 잊어버리면, collections.abc모듈이 실수한 부분을 알려준다.
from collections.abc import Sequence
class BadType(Sequence):
pass
# 오류가 나는 부분. 오류를 보고 싶으면 커멘트를 해제할것
foo = BadType()
TypeError Traceback (most recent call last)
in
1 # 오류가 나는 부분. 오류를 보고 싶으면 커멘트를 해제할것
----> 2 foo = BadType()
TypeError: Can't instantiate abstract class BadType with abstract methods getitem, len
sequenceNode에서 한 것 처럼 collections.abc에서 가져온 추상 기반 클래스가 요구하는 모든 메서드를 구현하면 index나 count와 같은 추가 메서드 구현을 거저 얻을 수 잇다.
class BetterNode(SequenceNode, Sequence):
pass
tree = BetterNode(
10,
left=BetterNode(
5,
left=BetterNode(2),
right=BetterNode(
6,
right=BetterNode(7))),
right=BetterNode(
15,
left=BetterNode(11))
)
print('7의 인덱스:', tree.index(7))
print('10의 개수:', tree.count(10))
7의 인덱스: 3
10의 개수: 1