プロパティー
オブジェクト指向プログラミング言語でのプロパティ ( property) は、オブジェクトへのアクセス方法のカプセル化を実現するための言語機能および構文のことをいいます。
Python でのプロパティーはオブジェクトの持つアトリビュートへのアクセス方法を定義するものと理解するとよいでしょう。
これまでに説明してきたように、Pythonではクラスのすべてのアトリビュートは公開されています。そのため、クラスの内部変数を非公開にしたいときは2つのアンダースコア(__)を変数名につけて擬似的に対応します。(非公開変数参照) code: 0901_attributes_as_pesudo_private.py
class Person():
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
p = Person('Freddie')
print(p.get_name())
print(dir(p))
この場合、p.__name として直接アクセスできないのはわかりますよね。
これをもう少しスマートに行うために property()関数を使います。
まずは、例を見てみましょう。
code: 0902_attrinutes_with_property.py
class Person:
def __init__(self, name_str):
print("init name")
self.__hidden_name = name_str
def get_name(self):
print("get_name called")
return self.__hidden_name
def set_name(self, name_str):
print(f"set_name called with {name_str}")
self.__hidden_name = name_str
name = property(get_name, set_name) # ここがポイント
p = Person("Freddie")
print(p.name)
p.set_name("David")
p.name = "Eddie"
print(dir(p))
クラスPerison に新しく name というアトリビュートをproperty()で作っています。
この関数は第1引数にアトリビュートから値を取得する関数、第2引数にはアトリビュートに値をセットする関数を与えます。 (詳細は後述します)
これを実行すると次のようになります。
code: bash
$ python 0902_attrinutes_with_property.py
init name
get_name called
Freddie
set_name called with David
set_name called with Eddie
'_Person__hidden_name', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_name', 'name', 'set_name' 重要な点は、p.name = 値 として直接アクセスしたとしても、そのアトリビュートに値をセットするための関数 set_name() が呼び出されているということです。
property() は次の引数を持つことができます。
書式: attr = property(getter, setter, deleter, doc)
getter() – アトリビュートを読み出すときに呼び出される関数
setter() – アトリビュートを書き込むときに呼び出される関数
deleter() – アトリビュートを削除するときに呼び出される関数
doc() – アトリビュートを説明する docstrings を含んだ文字列を返す関数
attr - 指定された getter、setter、deleterからのプロパティ属性を返します。
デコレータを使ってプロパティーを定義する
組み込み関数property()を使って定義するかわりに、デコレータとしてプロパティーを定義することができます。
code: 0903_decorator_as_property.py
class Person():
def __init__(self, name_str=''):
print('init name')
self.__hidden_name = name_str
@property
def name(self):
print('get name called')
return self.__hidden_name
@name.setter
def name(self, name_str):
print(f'set name called {name_str}')
self.__hidden_name = name_str
p = Person('Freddie')
print(p.name)
p.name = 'Eddie'
print(p.name)
@property のデコレータで修飾する関数名がデコレータ名で、この関数がgetter となります。@デコレータ名.setter として修飾した関数が setter となります。
このとき setter となる関数名はgetterと同じ名前になります。
setter が定義されていないプロパティーは読み取り専用となります。
組み込み関数として呼び出して定義する方法と、デコレーターとして呼び出して定義する方法とでは、どう使い分ければよいのでしょうか?
ひとつには、この読み取り専用としての定義がデコレータでは簡単になることがあります。