effective python#28

14
Effective Python 読読読 #4 読読 @Wizard_of_Oz__

Upload: bontakun

Post on 07-Feb-2017

75 views

Category:

Engineering


0 download

TRANSCRIPT

Page 1: Effective python#28

Effective Python読書会 #4

オズ @Wizard_of_Oz__

Page 2: Effective python#28

EffectivePython

項目 28カスタムコンテナ型は collections.abc を継承する

Page 3: Effective python#28

単純なユースケースの場合• 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

Page 4: Effective python#28

単純なユースケースの場合• 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 7After pop: [‘a’, ‘b’, ‘a’, ‘c’, ‘b’, ‘a’]Frequency: {‘a’ : 3, ‘c’ : 1, ‘b’ : 2}

Page 5: Effective python#28

継承しない場合• サブクラスではない形でセマンティクスを提供

– 二分木クラスにシーケンスのセマンティクスを提供

class BinaryNode(object): def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right

Page 6: Effective python#28

シーケンスの振る舞いの実装• Python はコンテナの振る舞いを

特別な名前を持ったインスタンスメソッドで実装

– シーケンスに添字でアクセス

bar = [1, 2, 3]bar[0]

– Python の解釈

bar.__getitem__(0)

Page 7: Effective python#28

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

Page 8: Effective python#28

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

Page 9: Effective python#28

BinaryNode クラスをシーケンスのように振る舞わせる• IndexableNode

– 通常の二分木として構築できる

– List のようにアクセスできる

print('LRR =', tree.left.right.right.value) #LRR = 7print('Index 0 =', tree[0]) #Index 0 = 2print('Index 1 =', tree[1]) #Index 1 = 5print('11 in the tree?', 11 in tree) #11 in the tree? Trueprint('17 in the tree?', 17 in tree) #17 in the tree? Falseprint('Tree is', list(tree)) #Tree is [2, 5, 6, 7, 10, 11, 15]

Page 10: Effective python#28

__getitem__ を実装した場合の問題点• シーケンスのセマンティクス全てを提供するには不十分

– __len__ という特別なメソッドが必要

len(tree)

>>>TypeError: object of type ‘IndexableNode’ has no len()

Page 11: Effective python#28

独自のコンテナ型を定義するのは困難• 他にも特別なメソッドの定義が必要

– count や index というメソッドが足りない

• collections.abc モジュール– 典型的なメソッドをすべて提供する抽象基底クラス

– 必要なメソッドの実装がないと教えてくれる

from collections.abc import Sequenceclass BadType(Sequence): pass

foo = BadType()>>>TypeError: Can’t instantiate abstract class BadType with abstract methods __getitem__, __len__

Page 12: Effective python#28

独自のコンテナ型を定義するのは困難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__

Page 13: Effective python#28

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 3print('Count of 10 is', tree.count(10)) #Count of 10 is 1

Page 14: Effective python#28

まとめ• 単純なユースケースは Python のコンテナ型から直接継承

– コンテナ型: list や dict など

• カスタムコンテナ型を正しく実装するには多数のメソッドが必要

• カスタムコンテナ型は collections.abc で定義されたインタフェースを継承する– 必要なインタフェースを備えていることを確かなものにするため