プロパティは非常に難しい概念です。
しかし、本などを見ても簡単に説明されている場合が多く、ただ使い方を暗記するだけにとどまってしまいがちです。
そこで、この記事ではプロパティの使い方について具体例と共に分かりやすく説明していきます。
プロパティの考え方について理解するためには、クラスの考え方について理解する必要があります。
また、属性への参照も理解している必要があります。
プロパティを使う目的
まず、プロパティを使う目的について整理しておきましょう。
Pythonでは、コードはなるべく簡潔に書き、無駄な関数は極力減らすという考えに基づいてコードを書いた方が良いと言われています。
Pythonにおける様々な機能が生まれた背景は様々ですが、プロパティを使うことによって、複雑なコードを少ない関数で書くことができるようになることは間違いありません。
ここでは、プロパティを使うことによって簡潔なコードを書くことができるようになる。ということを頭に入れておきましょう。
順を追ってpropertyを理解していく
propertyの考え方について、順番に整理していきましょう。
そもそもの話として、propertyというのは属性への参照をする時に使われる考え方です。
上記でも紹介していますが、属性への参照を理解していることが前提になります。
予めPythonに組み込まれている__getattr__や、__setattr__ではなく、自分で属性への参照や属性への代入を定義しようした場合、__get__メソッドと、__set__メソッドを使います。
そして、__get__メソッドが定義されていると、属性にアクセスされた時に__get__メソッドが呼び出され、属性に代入をした時に__set__メソッドが呼び出されます。
具体的なコードでみていきましょう
コード1 class Property():
2 def __get__(self, obj, objtype):
3 print('get')
4 def __set__(self, obj, val):
5 print('set')
6 class Propertycall():
7 x = Property()
8 a = Propertycall()
9 a.x
10 a.x = 1000
結果はこのようになります。
アウトプットget
set
コードの解説(1) 1行目でPropertyというクラスを定義しています。
そして、Propertyクラスには、__get__メソッドと__set__メソッドが定義されています。
(2) 6行目ではPropertycallというクラスを定義しています。このクラスはxというクラス変数を持っています。
(3) 8行目でaオブジェクトを作成しました。このオブジェクトは、クラス変数xにアクセスすることができます。
(ややこしいのですが、aオブジェクトから見ると、xは属性と呼ばれます。)
(4) 9行目でa.xと属性を参照しています。結果として、__get__メソッドが呼び出され、getが出力されます。
(5) 10行目でa.x = 1000と属性への代入を行っています。結果として、__set__メソッドが呼び出され、setが出力されます。
今回は__get__メソッドも__set__メソッドも文字列を出力しただけでした。
しかし、実際には__get__と__set__に以下のようなコードを書くことで、属性を取り出したり、属性にデータを代入することができます。
コードclass Property():
def __init__(self, val):
self.initial = val
def __get__(self, obj, objtype=None):
return self.initial
def __set__(self, obj, val):
self.initial = val
無事に属性への参照を実装することができましたが、これではコードが複雑で、あまりきれいではありません。
そこで使われるのがpropertyです。
propertyを使ってコードをすっきりさせる
上記のコードで、propertyを使って書いたコードがこちらです。
コード1 class Property():
2 def getget(self):
3 print('get')
4 def setset(self, obj):
5 print('set')
6 x = property( getget, setset)
7 a = Property()
8 a.x
9 a.x = 80
アウトプットget
set
6行目のpropertyがポイントです。propertyを使うと、属性にアクセスされた時の動きをコントロールすることができます。
具体的には、7行目でオブジェクトを作り、8行目で属性xを参照しています。そうすると、propertyがgetgetメソッドを実行するのです。
結果としてgetが出力されます。
ここでは、__get__メソッドではなく、任意の名前のメソッドでも良いことも意識しておきましょう(propertyが属性への参照からメソッドの実行までやってくれるのです)。
コードを更に簡潔にするのが@property
だいぶコードがすっきりとしてきました。
実は、Pythonは更にこのコードを更にすっきりとさせる機能が備えられています。
それが@propertyです。先程のコードでは、x = property(getget, setset)という形で、属性の名前としてxを使いましたが、@propertyを使うことによって、xを使わずに実装することができるのです。
実際にコードを書いてみます。
1 class Property():
2 @property
3 def getset(self):
4 print('get')
5 @setset.setter
6 def getset(self, obj):
7 print('set')
8 a = Property()
9 a.getset
10 a.getset = 80
出力される結果はさきほどのx = propertyを使った場合と同じですが、上記のように実装することによって、xを使わずに属性への参照を実現することができました。
@propertyは属性への参照と属性への代入ということが分かれば、あとは使い方(ルール)を覚えるだけです。@propertyを使う際のルールは以下の通りです。
getterとsetterでメソッドの名前を同じにする(このコードの場合、getset)。
getterの前には@propertyを付けて、setterの前には@メソッド名.setterという名前を付ける。
@property文の書き方例
最後に、実際にgetterとsetterの機能を実装した形で、@propertyを含んだコードをご紹介しますので、参考にして下さい。
コード1 class NewClass():
2 def __init__(self, number):
3 self.name = number
4 @property
5 def home(self):
6 return self.name
7 @home.setter
8 def home(self, number):
9 self.name = number
10 print(self.name)
11 t = NewClass(100)
12 t.home
13 t.home = 12
アウトプット100
12
引数が沢山あって少し分かりづらいのですが、順を追っていくと、少しずつ@propertyを使った文の書き方のイメージがわいてくるでしょう。