今さらPython3 (40) - クラスの続き
ついに40個目の記事。第6章の続きです。
- 作者: Bill Lubanovic,斎藤康毅,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
set/getの話
Pythonの場合は、Public/Private/Protectedみたいな区分がないから、クラスに属するメソッドやプロパティは丸見え。
>>> class Duck(): ... def __init__(self, input_name): ... self.hidden_name = input_name ... def get_name(self): ... print('inside the getter') ... return self.hidden_name ... def set_name(self, input_name): ... print('inside the setter') ... self.hidden_name = input_name ... name = property(get_name, set_name) ... >>>
前提として、hidden_nameという触らせたくない(=触って欲しくない)属性があります。get_name/set_nameってのは、いわゆるgetter/setterメソッドに相当して、他の言語だったら(もちろんPythonでもできる)、これらのメソッドを呼んで、属性値を取得したり、逆に新しい値を割り当てる。Pythonicに行きたいなら、propertyを使おうってことだね。
>>> fowl = Duck('Howard') >>> fowl.name inside the getter 'Howard' >>> fowl.get_name() inside the getter 'Howard' >|| 最初の1行は初期化だから、__init__でhidden_nameに'Howard'という値が渡る。2番目のやつはclassの最後の行にあったproperty経由で、裏でget_name()が呼ばれてhidden_nameの値がname(hidden_nameじゃないのがポイント)に渡される。最後のは、直接getterメソッドを呼んでる。動作比較目的だよね。 >|python| >>> fowl.name = 'Daffy' inside the setter >>> fowl.name inside the getter 'Daffy' >>> fowl.set_name('Daffy2') inside the setter >>> fowl.name inside the getter 'Daffy2' >>>
丁寧に読めばそんなに難しくない。nameというproperty(hidden_nameじゃないのがポイント)に'Daffy'という値を渡しているけど、裏側でset_name()が呼ばれてhidden_nameの値として設定される。その次のはsetterを直接呼んでいるパターン。
次の例はどうなっているかというと、、、
>>> class Deck(): ... def __init__(self, input_name): ... self.hidden_name = input_name ... @property ... def name(self): ... print('inside the getter') ... return self.hidden_name ... @name.setter ... def name(self, input_name): ... print('inside the setter') ... self.hidden_name = input_name ... >>>
@propertyはgetter用(getterなんちゃらがないから)、@name.setterはsetter用というのは分かる。@で始まるのはデコレータでしたね。直後のdefの後ろの名称がnameになっていて同じなので、対になっているんだろうなと想像できる。
>>> fowl = Duck('Howard') >>> fowl.name inside the getter 'Howard' >>> fowl.name = 'Donald' inside the setter >>> fowl.name inside the getter 'Donald'
動きはさっきと同じですね。ここでちょいと確認。@name.setterのnameのところは固定なのか?ほら、直下のdefのところにpropertyの名前を指定しているから分かりにくいじゃん。
>>> class Cat(): ... def __init__(self, input_name): ... self.secret_name = input_name ... @property ... def i_name(self): ... return self.secret_name ... @i_name.setter ... def i_name(self, input_name): ... self.secret_name = input_name ... >>> mew = Cat('tama') >>> mew.i_name 'tama' >>> mew.i_name = 'mewmew' >>> mew.i_name 'mewmew' >>>
つまり、@プロパティ名.setterが正しい表現ですね。
>>> class Circle(): ... def __init__(self, radius): ... self.radius = radius ... @property ... def diameter(self): ... return 2 * self.radius ... >>> c = Circle(5) >>> c.radius 5 >>> c.diameter 10 >>>
これまでの例が、setter/getter的な感じで値を設定する、取り出すというところだけにフォーカスしていたけど、上の例だとdiameter()に計算式が入っていますと。
>>> c.radius = 7 >>> c.diameter 14
計算式があるので、radiusの値を変えただけで、明示的にdiameterの値を変えなくても自動で更新されている。(呼び出したときにその場で計算しているのかな?)
>>> c.diameter = 20 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute >>>
@xxxx.setterを定義しない場合は、直接値を設定することは出来ないという説明ですね。
先頭にアンダースコア2つつけると
前回、消化不良になった話の続きですね。属性の頭にアンダースコア2つを並べるとクラス定義の外からは見えなくなるらしい。
>>> class Duck(): ... def __init__(self, input_name): ... self.__name = input_name ... @property ... def name(self): ... print('inside the getter') ... return self.__name ... @name.setter ... def name(self, input_name): ... print('inside the setter') ... self.__name = input_name ...
これは、さっきのクラスDuck。一応動作確認からの、、、
>>> fowl = Duck('Howard') >>> fowl.name inside the getter 'Howard' >>> fowl.name = 'Donald' inside the setter >>> fowl.name inside the getter 'Donald' >>> fowl.__name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Duck' object has no attribute '__name' >>>
__nameなんて属性はないよと怒られる。なんかエラー内容が怪しい。保護されている感がメッセージから伝わらないw。
>>> fowl._Duck__name
'Donald'
実際にはProtectするんではなくて、名前を少しいじって、当てずっぽうで使われないようにするというイメージの方が近そうだね。本の中では、マングリングという用語を使っている。
マングリング(name mangling)って何?という場合はこれを。
上の命名ルールと名前修飾という日本語を見れば意味はイメージしやすいよね。
(つづく)