はじめに
Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!
今回学習する教材
-
- 8章構成
- 本章216ページ
今日の進捗
- 進行状況:73-78ページ
- 第3章:クラスと継承
- 本日学んだことの中で、よく忘れるところ、知らなかったところを書いていきます。
多重継承はmix-inユーティリティクラスだけに使う
多重継承はあまり使うべきではなく、代わりにmix-inを使うべきです。
mix-inとは
クラスが提供すべき一連の追加のメソッドを定義するだけの小さなクラスのことです。
また、通常のクラスと異なり、インスタンス属性を持たず、__init__
コンストラクタを呼び出す必要もないです。
mix-inの例を、継承した任意のクラスで追加される新たなメソッドとして次のように定義します。
class ToDictMixin(object):
def to_dict(self):
'''
このオブジェクトの属性を辞書にして返す
'''
return self._traverse_dict(self.__dict__)
def _traverse_dict(self, instance_dict):
'''
辞書を受け取り、新たな辞書outputを返す。
Parameters
----------
instance_dict: dict
Returns
-------
output : dict
キーに、instance_dictのキー、値に_traverse()
'''
output = {}
for key, value in instance_dict.items():
output[key] = self._traverse(key, value)
return output
def _traverse(self, key, value):
'''
辞書の値の型に応じて、関数を呼び出す。
'''
if isinstance(value, ToDictMixin):
return value.to_dict()
elif isinstance(value, dict):
return self._traverse_dict(value)
elif isinstance(value, list):
return [self._traverse(key, i) for i in value]
elif hasattr(value, '__dict__'):
return self._traverse_dict(value.__dict__)
else: return value
次に、このmix-inを使っ2分木の辞書表現を作るクラスを定義し、オブジェクトの属性を出力します。
class BinaryTree(ToDictMixin):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
tree = BinaryTree(5,
left=BinaryTree('aaa', right=BinaryTree(4)),
right=BinaryTree(2.4, left=BinaryTree(8)))
# treeオブジェクトの属性を辞書にして表示
print(tree.to_dict())
mix-inの長所は、特定の型に依存せず、必要な時に機能をオーバーライドできる点です。例えば、先ほどの BinaryTreeのサブクラスを親への参照を保持するようにオーバーライドします。
class BinaryTreeWithParent(BinaryTree):
def __init__(self, value, left=None,
right=None, parent=None):
super().__init__(value, left=left, right=right)
self.parent = parent
# デフォルトの実装だと永久にループするため、必要な値だけを処理するように変更
def _traverse(self, key, value):
'''
値の型がBinaryTreeWithParent属性かつ、キーが親クラスだった場合、value.valueを返し、
それ以外の場合は、親クラスの_traverseと同じ処理をするように変更
'''
if (isinstance(value, BinaryTreeWithParent) and
key == 'parent'):
return value.value # サイクルを防ぐ
else:
return super()._traverse(key, value)
見やすくするために、pprintで出力する
import pprint
root = BinaryTreeWithParent(5)
root.left = BinaryTreeWithParent(3, parent=root)
root.left.right = BinaryTreeWithParent(13, parent=root.left)
pprint.pprint(root.to_dict())
出力結果
{'left': {'left': None,
'parent': 5,
'right': {'left': None, 'parent': 3, 'right': None, 'value': 13},
'value': 3},
'parent': None,
'right': None,
'value': 5}
BinaryTreeWithParent._traverse を定義することで、BinaryTreeWithParent型の属性を持つすべてのクラスでも、ToDictMixinが自動的に働きます。
class NamedSubTree(ToDictMixin):
def __init__(self, name, tree_with_parent):
self.name = name
self.tree_with_parent = tree_with_parent
mytree = NamedSubTree('bbb',root.left.right)
pprint.pprint(mytree.to_dict())
出力結果
{'name': 'bbb',
'tree_with_parent': {'left': None, 'parent': 3, 'right': None, 'value': 13}}
まとめ
- 基本的には、多重継承よりmix-inを使う
- mix-inクラスが必要なときに、クラスごとにカスタマイズする