今さらPython3 (40) - クラスの続き

ついに40個目の記事。第6章の続きです。

入門 Python 3

入門 Python 3

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)って何?という場合はこれを。

名前修飾 - Wikipedia

上の命名ルールと名前修飾という日本語を見れば意味はイメージしやすいよね。

(つづく)