Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Effective python#28

227 views

Published on

Effective python#28

Published in: Engineering
  • Be the first to comment

Effective python#28

  1. 1. Effective Python 読書会 #4 オズ@Wizard_of_Oz__
  2. 2. Effective Python 項目28 カスタムコンテナ型はcollections.abcを継承する
  3. 3. 単純なユースケースの場合 • Listにメソッドを加えたい – 要素の頻度を数えるメソッド – Listを継承したサブクラスを作る class FrequencyList(list): def __init__(self, members): super().__init__(members) def frequency(self): counts = {} for item in self: counts.setdefault(item, 0) counts[item] += 1 return counts
  4. 4. 単純なユースケースの場合 • Listを継承したサブクラスを作る – Listの標準機能がすべて使える foo = FrequencyList([‘a’, ‘b’, ‘a’, ‘c’, ‘b’, ‘a’, ‘d’]) print (‘Length is’, len(foo)) foo.pop() print(‘After pop:’, repr(foo)) print(‘Frequency:’, foo.frequency()) >>> Length is 7 After pop: [‘a’, ‘b’, ‘a’, ‘c’, ‘b’, ‘a’] Frequency: {‘a’ : 3, ‘c’ : 1, ‘b’ : 2}
  5. 5. 継承しない場合 • サブクラスではない形でセマンティクスを提供 – 二分木クラスにシーケンスのセマンティクスを提供 class BinaryNode(object): def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right
  6. 6. シーケンスの振る舞いの実装 • Pythonはコンテナの振る舞いを 特別な名前を持ったインスタンスメソッドで実装 – シーケンスに添字でアクセス bar = [1, 2, 3] bar[0] – Pythonの解釈 bar.__getitem__(0)
  7. 7. BinaryNodeクラスを シーケンスのように振る舞わせる • __getitem__の実装を提供する – 二分木クラスにシーケンスのセマンティクスを提供 class IndexableNode(BinaryNode): def _search(self, count, index): .... # Returns (found, count) def __getitem__(self, index): found, _ = self._search(0, index) if not found: raise IndexError('Index out of range') return found.value
  8. 8. BinaryNodeクラスを シーケンスのように振る舞わせる class IndexableNode(BinaryNode): def _search(self, count, index): found = None if self.left: found, count = self.left._search(count, index) if not found and count == index: found = self else: count += 1 if not found and self.right: found, count = self.right._search(count, index) return found, count # Returns (found, count) def __getitem__(self, index): found, _ = self._search(0, index) if not found: raise IndexError('Index out of range') return found.value
  9. 9. BinaryNodeクラスを シーケンスのように振る舞わせる • IndexableNode – 通常の二分木として構築できる – Listのようにアクセスできる print('LRR =', tree.left.right.right.value) #LRR = 7 print('Index 0 =', tree[0]) #Index 0 = 2 print('Index 1 =', tree[1]) #Index 1 = 5 print('11 in the tree?', 11 in tree) #11 in the tree? True print('17 in the tree?', 17 in tree) #17 in the tree? False print('Tree is', list(tree)) #Tree is [2, 5, 6, 7, 10, 11, 15]
  10. 10. __getitem__を実装した場合の問題点 • シーケンスのセマンティクス全てを提供するには不十分 – __len__という特別なメソッドが必要 len(tree) >>> TypeError: object of type ‘IndexableNode’ has no len()
  11. 11. 独自のコンテナ型を定義するのは困難 • 他にも特別なメソッドの定義が必要 – countやindexというメソッドが足りない • collections.abcモジュール – 典型的なメソッドをすべて提供する抽象基底クラス – 必要なメソッドの実装がないと教えてくれる from collections.abc import Sequence class BadType(Sequence): pass foo = BadType() >>> TypeError: Can’t instantiate abstract class BadType with abstract methods __getitem__, __len__
  12. 12. 独自のコンテナ型を定義するのは困難 try: from collections.abc import Sequence class BadType(Sequence): pass foo = BadType() except: logging.exception('Expected') else: assert False >>> TypeError: Can’t instantiate abstract class BadType with abstract methods __getitem__, __len__
  13. 13. collections.abc • 抽象基底クラスに必要な全てのメソッドを実装 – 追加メソッドが提供される(indexやcountなど) – 複雑な型(SetやMutableMapping等)で特に有用 – 実装が必要な特殊メソッドの個数が膨大 class BetterNode(SequenceNode, Sequence): pass tree = BetterNode(...) print('Index of 7 is', tree.index(7)) #Index of 7 is 3 print('Count of 10 is', tree.count(10)) #Count of 10 is 1
  14. 14. まとめ • 単純なユースケースはPythonのコンテナ型から直接継承 – コンテナ型:listやdictなど • カスタムコンテナ型を正しく実装するには多数のメソッドが必要 • カスタムコンテナ型はcollections.abcで定義されたインタフェースを継承する – 必要なインタフェースを備えていることを確かなものにするため

×