今回は、Pythonのsetterとgetterの使い方をご紹介します。
setterとgetterの概要
Pythonでsetterやgetterと呼ばれる仕組みは、クラス内の属性を外部から取得・変更する際に、中間処理を挟むための方法です。
多くの言語ではカプセル化を行うためにsetterやgetterを使用しますが、Pythonの場合、一般的に@propertyデコレータやproperty()関数を利用することが多いです。
これらを活用することで、属性の変更時にバリデーション処理(値の検証など)や変換処理を行うことが可能になります。
Pythonにおけるsetterとgetterの実装方法
Pythonでは、以下の3つの方法でsetterとgetterを実装できます。
- @propertyデコレータを使う方法
- property()関数を使う方法
- ディスクリプタを用いる高度な方法
ここでは、それぞれの実装例を紹介します。
@propertyデコレータによる実装
まずは@propertyデコレータを使った方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Temperature: def __init__(self, celsius): self._celsius = celsius # 内部属性をアンダースコア付きで定義 @property def celsius(self): """摂氏温度のgetter""" return self._celsius @celsius.setter def celsius(self, value): # 値の範囲をチェックして不正なら例外を投げる if value < -273.15: raise ValueError("摂氏温度は絶対零度以上でなければなりません") self._celsius = value @property def fahrenheit(self): """華氏温度を返す(読み取り専用プロパティ)""" return self._celsius * 9 / 5 + 32 # 使用例 temp = Temperature(25) print(temp.celsius) # getterを呼び出す print(temp.fahrenheit) # fahrenheitは読み取り専用 temp.celsius = 30 # setterを呼び出す |
このコードでは、内部属性に直接アクセスさせず、検証・変換のための処理を@propertyデコレータで提供しています。
25
77.0
property()関数による実装
@propertyデコレータを使わず、組み込みのproperty()関数を利用することも可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Person: def __init__(self, name): self._name = name def get_name(self): return self._name def set_name(self, value): # 文字列以外が代入されないようにチェック if not isinstance(value, str): raise TypeError("名前は文字列でなければなりません") self._name = value def del_name(self): del self._name # property()を使って、getter, setter, deleterをまとめる name = property(get_name, set_name, del_name, "名前を表すプロパティ") # 使用例 p = Person("Alice") print(p.name) # getterを呼び出す p.name = "Bob" # setterを呼び出す del p.name # deleterを呼び出す |
property()関数は、関数を引数に渡すスタイルのため、冗長に感じる場合があります。
Alice
ディスクリプタを利用した高度な方法
より柔軟な実装を行いたい場合、ディスクリプタと呼ばれる仕組みを使うこともできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Descriptor: def __init__(self, name): self.name = name def __get__(self, instance, owner): # クラスから呼ばれた場合はディスクリプタオブジェクト自身を返す if instance is None: return self return instance.__dict__.get(self.name) def __set__(self, instance, value): # setter相当の動作を実装 # 例: ここで値のバリデーションなどが可能 instance.__dict__[self.name] = value def __delete__(self, instance): del instance.__dict__[self.name] class MyClass: # Descriptorクラスを使ってプロパティを定義 attr = Descriptor("attr") def __init__(self, attr): self.attr = attr # 使用例 obj = MyClass(100) print(obj.attr) obj.attr = 200 print(obj.attr) del obj.attr |
ディスクリプタを使うと、getterやsetterのロジックを一か所にまとめて、多くのクラスで再利用できます。
100
200
よくあるエラーや注意点
setterやgetterを使う上で、以下の点に注意するとスムーズに実装できます。
- 無限再帰の回避:setterやgetterの内部で、同じプロパティを呼び出さないようにする
- 命名規則:内部属性にはアンダースコア付き(_value)の名前を使う
- 必要以上の複雑化を避ける:単純な属性にはgetter/setterを使わず、後から必要になったらプロパティに切り替える
- パフォーマンスの考慮:頻繁にアクセスする属性に複雑な処理を組み込むと、実行速度に影響が出る
まとめ
Pythonでは、setterやgetterを簡潔に実装する方法として@propertyデコレータやproperty()関数があります。
ディスクリプタを使えば、より柔軟で再利用性の高い実装が可能です。
カプセル化やバリデーションが必要な場合にこれらを活用すると、保守性の高いコードを書くことができます。