リンク
はじめに
Python チュートリアルで取り扱われていない「プロパティ」についてのまとめです。
僕は現在Pythonで研究のコードを書いていますが、如何せん適当に書いてしまっていたため、グチャグチャのギタギタになってしまいました。
そこで、Pythonの中級者を目指し、きちんと誰が読んでも分かるようなコードを目指そうと思います。
プロパティって?
そもそもプロパティってなんでしょうか。
プロパティは、オブジェクトの属性・性質データ
を表します。
...よく分かりませんね。
例えば、二次元のシューティングゲームで使用するクラスPlayerについて考えてみます。
Playerインスタンスは、シューティングゲームで移動したり、描画をするために
- 横座標
x
- 縦座標
y
の情報を持っています。
このように、各インスタンスが持つ属性・性質のデータをプロパティと呼びます。
Playerを実装してみる
とりあえず、通常のPlayerクラスを実装してみます。
各座標と移動するmove関数を用意しましょう。
今回のPlayerは、$x, y$ともに$0{\leqq}x, y{\leqq}500$までを移動可能範囲とします。
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'(x = {self.x}, y = {self.y})'
def move(self, dx, dy):
self.x += dx
self.y += dy
"""
(x = 100, y = 100)
"""
player = Player(100, 100)
print(player)
"""
(x = 0, y = -100)
"""
player.move(-100, -200)
print(player)
上のソースコードは普通に動きますし、シンプルです。
しかし、オブジェクト指向という点で、上記のソースコードには問題点がいくつか存在します。
問題点1 カプセル化
上記のクラスは、直接プロパティ変数をいじることができてしまいます。
これはあまり良い状態ではありません。
不都合な変数の設定・変更の可能性が存在するためです。
また、メソッドによって、状態が変化する というのがオブジェクト指向の基本です。
問題点2 範囲外
このPlayerクラスは、$x, y$ともに$0{\leqq}x, y{\leqq}500$の中で動いてほしいです。
しかし、上記のmove関数によって、範囲外に出てしまいました。
なんとかして、変数の設定と同時に、範囲内に収める処理が設定できないでしょうか?
プロパティ
そこで、Pythonの記法プロパティを利用します。
以下のようにソースコードを書き換えてみましょう。
class Player:
MIN_X = 0
MAX_X = 500
MIN_Y = 0
MAX_Y = 500
def __init__(self, x, y):
self._x = x
self._y = y
def __repr__(self):
return f'(x = {self.x}, y = {self.y})'
@property
def x(self):
return self._x
@x.setter
def x(self, dx):
self._x += dx
self._x = max(0, min(self.MAX_X, self._x))
@property
def y(self):
return self._y
@y.setter
def y(self, dy):
self._y += dy
self._y = max(0, min(self.MAX_Y, self._y))
def move(self, dx, dy):
self.x += dx
self.y += dy
player = Player(100, 100)
player.move(-200, -200)
"""
(x = 0, y = 0)
"""
print(player)
一つ一つ中身を見ていきます。
init
initでは、変数の初期化を_xで行っています。
内部のみで扱いたい変数は_xなど、アンダースコアを使って表現することが多いようです。
@property
@property
def x(self):
return self._x
@property
def 外部公開変数名(self):
return 内部変数
次に、x(self)
を見てみます。
これは、メソッドのように機能していますが、クラスの外に対してはただの変数
のように見せることができます。
これは値を外部に返すためのものです。
このようになるのは、この関数定義の上に@property
というデコレータという記法を利用しているためです。
デコレータに関しては、また今度記事を書こうと思います。
とりあえず、@propertyというキーワードをメソッドにつけることで、そのメソッド名でクラス外部から値を取得できます。
@外部公開変数名.setter
@x.setter
def x(self, dx):
self._x += dx
self._x = max(0, min(self.MAX_X, self._x))
@外部公開変数名.setter
def 外部公開変数名(self, dx):
self.内部変数 += dx
self.内部変数 = max(0, min(self.MAX_X, self._x))
先程の@propertyは、クラスの外から値を取得して利用するためのキーワードでした。
ということは、外から値を与えて内部に値をset
するキーワードも存在します。
@外部公開変数名.setter
というキーワードを利用します。
これによって、通常のような記法で内部に値を設定できます。
プロパティの嬉しさ
プロパティの嬉しさはなんでしょうか?
実は、Pythonのプロパティは他の言語と異なる形をしています。
もともと自分はC#やC++、Javaを触っていたため、かなりびっくり・違和感を感じました。
Pythonのプロパティは、
- 外部にとっては、変数の見た目そのままで利用できる
- 内部にとっては、大切な変数を隠して、追加処理を記述できる
という嬉しさがあります。
他の言語では、
player.setValueA(50);
のように、関数・メソッドの形で記述しなければならず、言ってしまえば冗長でした。
Pythonではデコレータという動的言語ならではの言語仕様を利用することで、
外部にうまく変数のようにメソッドを公開
内部では、追加処理を書くことができます。
ただ、setter内で複雑な内容を書きすぎるとバグになる・意識しなければいけなくなるため、軽い内容にするべきかもしれません。
最後に
プロパティについてまとめてみました!
初心者のため、間違っている部分が多いかと思います。
ぜひ、アドバイス・ご意見をいただけると嬉しいです!