【Python】クラスのプロパティ(getter/setter)の書き方【入門第35回】
目次
クラスのプロパティ(property)とは?
こんにちは、narupoです。
今回もクラスの機能ですが、プロパティという機能についてやっていきます。
プロパティはクラスのインスタンス変数へのゲッターやセッターを書くのに非常に便利な機能です。
このプロパティを使うとより綺麗にクラスを設計できると言えるでしょう。
具体的には↓を見ていきます。
プロパティの必要性
プロパティの書き方
ゲッター(getter)
セッター(setter)
プロパティの必要性
プロパティの必要性についてです。
まず以下のようなクラスがあるとします。
class Cat: def __init__(self): self.name = 'ミケ'
↑のクラスCat
のインスタンス変数name
にアクセスするには↓のような方法が考えられます。
cat = Cat() cat.name = 'タマ'
これはこれで簡便で便利です。
しかし、この設計には問題があります。
たとえば、クラスの設計を進めていくうちに、インスタンス変数name
の変数名を、たとえばcute_name
などに変えたくなったとします。
そこで思い切りがいいあなたは変数名を変更しました。
class Cat: def __init__(self): self.cute_name = 'ミケ'
ここで問題が発生します。
Cat
のインスタンスを使っている他のところでは、変更前のインスタンス変数name
を使っている可能性があるからです。
# 他ではnameを参照してるよ~ cat = Cat() print(cat.name)
name
はcute_name
に変更してしまったので、従来のname
を使っているコードは動かなくなってしまいます。
もちろん他のコードも変更すればいいのですが、変更箇所が100
個とかになるとゲンナリしてきませんか?
実際に、プログラミングではそういう事態がありえるのです。
従来のゲッター、セッター
そこで、こういう事態を避けるため、旧来の方法ではゲッターやセッターを定義するのが普通でした。
ゲッターというのは、変数の値を取得するだけのメソッドです。
たとえば↓のようにです。
class Cat: def __init__(self): self.name = 'ミケ' def get_name(self): return self.name cat = Cat() print(cat.get_name())
↑のget_name
というメソッドが俗にゲッターと呼ばれるものです。
このゲッターを使ってインスタンス変数にアクセスするようにしていれば、たとえインスタンス変数name
の名前がcute_name
になったとしても、他のコードには影響が及びません。
class Cat: def __init__(self): self.cute_name = 'ミケ' def get_name(self): return self.cute_name cat = Cat() print(cat.get_name())
変数に値をセットしたい時は、セッターというものを作ります。
たとえば↓のようにです。
class Cat: def __init__(self): self.name = 'ミケ' def set_name(self, name): self.name = name cat = Cat() cat.set_name('タマ')
↑のset_name
というメソッドが俗にセッターと呼ばれるものです。
こうすることで、先ほどのゲッターと同じようにインスタンス変数名が変更されても他のコードには影響を与えずに済みます。
プロパティによるゲッター、セッター
旧来のゲッター、セッターは、たしかに便利なのですが、できれば↓のように書けると一番楽です。
cat = Cat() print(cat.name) cat.name = 'タマ'
つまり、インスタンス変数に直接アクセスするのが一番書いててわかりやすいし、楽だということですね。
しかし、この方法だと変数名の変更などに対応できません。
この書き方でかつ、変数名の変更などに対応できれば最高ということになります。
これを実現するのがプロパティです。
つまり、プロパティを使えば、↓のようにインスタンス変数にアクセスできるのです。
cat = Cat() print(cat.name) cat.name = 'タマ'
もちろん、Cat
内部のインスタンス変数の変数名は自由に変更できますし、その影響もクラスの外には伝わりません。
なかなか魔法みたいな機能ですが、さっそくプロパティの書き方を紹介したいと思います。
プロパティの書き方
プロパティは↓のように書きます。
@property def プロパティ名(self): pass
たとえば↓のようなクラスがあります。
class Cat: def __init__(self): self.name = 'ミケ'
このクラスのname
変数にプロパティを設定したい場合は↓のようにします。
class Cat: def __init__(self): self.__name = 'ミケ' @property def name(self): pass
インスタンス変数name
がプライベート変数になって、__name
に変わっていることに注意してください。
これはプロパティ名との衝突を避けるためです。
これでプロパティの設定は完了ですが、これだけではゲッターやセッターは使えません。
ゲッター(getter)
プロパティのゲッターを作るには↓のようにします。
@プロパティ名.getter def プロパティ名(self): return self.変数名
さきほどのCat
クラスにゲッターを書くには↓のようにします。
class Cat: def __init__(self): self.__name = 'ミケ' @property def name(self): pass @name.getter def name(self): return self.__name
↓の部分がゲッターを書いている所です。
@name.getter def name(self): return self.__name
このゲッターを使うには↓のようにします。
cat = Cat() print(cat.name)
従来のインスタンス変数へのアクセス方法と代わってませんね。
↑の出力結果はミケ
になります。
本当にさっきのゲッターが呼ばれているのでしょうか?
↓のようにprint
を仕込んでみましょう。
@name.getter def name(self): print('ゲッターが呼ばれました') return self.__name
これで実行すると↓のような結果になります。
ゲッターが呼ばれました ミケ
ちゃんとゲッターが呼ばれているようですね。
ゲッターの省略
ちなみに、先ほどのゲッターですが↓のように省略することができます。
class Cat: def __init__(self): self.__name = 'ミケ' @property def name(self): return self.__name
@property
のところに直接ゲッターの処理内容を書いています。
実行結果は先ほどの結果と同じです。
セッター(setter)
変数に値を設定するセッターを書くには↓のようにします。
@プロパティ名.setter def プロパティ名(self, val): self.変数名 = val
例によってCat
クラスを例にすると↓のようになります。
class Cat: def __init__(self): self.__name = 'ミケ' @property def name(self): pass @name.setter def name(self, name): self.__name = name
↑の↓の部分がセッターを書いている所です。
@name.setter def name(self, name): self.__name = name
このようにセッターを書いておくと、↓のようにしてセッターを呼び出すことができます。
cat = Cat() cat.name = 'タマ'
↑の場合、タマ
という文字列が、セッターの引数name
に渡されます。
見た感じはただの代入ですが、しっかりセッターが呼ばれています。
セッター内で値のバリデーション
セッターを書くと値のバリデーションを書けるようになります。
たとえば↓のように。
@name.setter def name(self, name): if name is None: raise TypeError('invalid name') self.__name = name
↑のように変数に値をセットする前に、セットする値が正しいかどうか検証(バリデート)します。
こうすることで、よりクラスの設計が堅牢になります。
たとえば↓のようにname
にNone
を入れようとすると、
cat = Cat() cat.name = None
↓のようにTypeError
が発生します。
TypeError: invalid name
おわりに
Pythonのクラスのプロパティ機能は、非常に便利で使い勝手のいい機能です。
ぜひ覚えて使えるようになっておきましょう。
以上、次回に続きます。
また見てね
関連動画