6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【中級者への道】Pythonのプロパティを理解する

Last updated at Posted at 2019-11-06

リンク

【中級者への道】記事リンクまとめ

はじめに

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内で複雑な内容を書きすぎるとバグになる・意識しなければいけなくなるため、軽い内容にするべきかもしれません。

最後に

プロパティについてまとめてみました!
初心者のため、間違っている部分が多いかと思います。
ぜひ、アドバイス・ご意見をいただけると嬉しいです!

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?