『Effective Python』の続き。Pythonにプライベートなどない。 www.effectivepython.com
「Pythonには、パブリックとプライベートという2つのクラス属性があります。(超訳)」からこのItemは始まるが、基本的にはパブリックを使いましょうという話。
プライベートなメソッドや属性には名前の接頭辞にアンダースコア__
を2つ以上つける。
プライベートメンバはクラス内で__name名
から_class名__name名
に置換されるだけなのでこのカラクリを知っていると外から簡単にアクセスできてしまう。
また、置換方法にクラス名が含まれているので、このカラクリを利用したままリファクタリングなどでクラスの階層構造を変更してしまうとAttributeError
が起きてしまう。
例えば、
class MyClass(object): def __init__(self, value): self.__value = value def get_value(self): return str(self.__value) class MyIntegerSubclass(MyClass): def get_value(self): return int(self._MyClass__value) foo = MyIntegerSubclass(5) assert foo.get_value() == 5
という構造だったのに、
class MyBaseClass(object): def __init__(self, value): self.__value = value def get_value(self): return self.__value class MyClass(MyBaseClass): def get_value(self): return str(super().get_value()) class MyIntegerSubclass(MyClass): def get_value(self): return int(self._MyClass__value) foo = MyIntegerSubclass(5) foo.get_value()
のようにすると破綻してしまう。
そこで、アンダースコアを1つだけつけて(筆者はプロテクト属性と呼んでいる)、内部で利用していることを示して、さらにその旨をドキュメントとして残すこと、と提案している。
本当にプライベート属性を使うタイミングとは、親クラスと子クラスで属性の名前が衝突する可能性があるとき、としている。
class ApiClass(object): def __init__(self): self.__value = 5 def get(self): return self.__value class Child(ApiClass): def __init__(self): super().__init__() self._value = 'hello' # OK! a = Child() print(a.get(), 'and', a._value, 'are different')
このItemは、PEP 8の「継承の設計」 はじめに — pep8-ja 1.0 ドキュメント に書いてある内容を詳しく迫ったItemと見なしてよいだろうか。