Pythonのプロパティは、属性のカプセル化を優雅に実現する方法として登場しました。他の言語(Java/C++)のgetterやsetterと類似していますが、メソッドを明示的に呼び出す必要がありません。propertyが登場する前は、変数にアクセスするために明示的なget/setメソッドを実装する必要があり、クラスのインターフェースが複雑化し、煩雑になっていました。
問題は、クラスのユーザーが属性(obj.xを使用)と透明なインターフェースでやり取りできるようにしつつ、内部で計算、検証、キャッシュ、または値のバリデーションのロジックを制御できるようにすることです。propertyがないと、内部の実装を変更するたびにすべての呼び出しを変更する必要があり、大量のエラーや拡張性の低下につながります。
解決策として、@propertyデコレーターを使用することで、メソッドを属性に変換し、計算、検証、または遅延ロードのロジックをメソッド内に隠しながら、クラスの外部インターフェースを変更せずに実現します。getterは読み取り専用として実装され、.setterを使用して書き込みを整理し、.deleterは削除します。
コード例:
class Rectangle: def __init__(self, width, height): self._width = width self._height = height @property def area(self): # 'area'を属性として読み取ることができます return self._width * self._height @property def width(self): return self._width @width.setter def width(self, value): # rect.width = ...の代入時に自動的に呼び出されます assert value > 0, "Width must be positive" self._width = value rect = Rectangle(3, 4) print(rect.area) # 12 rect.width = 10 print(rect.area) # 40
主な特徴:
propertyはクラスメソッドや静的メソッドと一緒に働くことができますか?
いいえ。propertyはインスタンスレベル(インスタンス)のプロパティのみを作成します。クラスレベルでは、特別なデスクリプタや@classmethod-propertyを使用してください(手動で実装されます)。
setterなしでgetterのみのpropertyを宣言した場合、属性に代入できますか?
いいえ。そのようなpropertyは読み取り専用です。代入しようとするとAttributeErrorが発生します。
setter内で例外が発生した場合(例えば、assert)、どうなりますか?
例外が外部に発生し、代入は行われず、属性の値は変更されません。これはバリデーションによく使用されます。
ネガティブケース:クラスの公開属性、制御なしでの変更。
利点:速くて簡単。
欠点:クライアントコードを再記述せずに内部ロジックを変更することができず、一貫性のない状態になりがちです。
ポジティブケース:propertyを通じてカプセル化、getter/setter内のロジック、厳格な検証。
利点:柔軟性、制御、透明性、安全なAPIの進化。
欠点:若干のコード増加、デバッグ時の複雑性の増加。