Python @property デコレーター
Python @property デコレータ
このチュートリアルでは、Python @property デコレーターについて学びます。オブジェクト指向プログラミングでゲッターとセッターを使用する Pythonic な方法。
Python プログラミングは組み込みの @property
を提供します オブジェクト指向プログラミングで getter と setter をより簡単に使用できるようにするデコレータ
@property
について詳しく説明する前に デコレータは、そもそもなぜそれが必要なのかについて直感を構築しましょう。
ゲッターとセッターのないクラス
温度を摂氏で保存するクラスを作成するとします。また、温度を華氏に変換するメソッドも実装します。これを行う 1 つの方法は次のとおりです。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
このクラスからオブジェクトを作成し、temperature
を操作できます。 属性:
# Basic method of setting and getting attributes in Python
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# Create a new object
human = Celsius()
# Set the temperature
human.temperature = 37
# Get the temperature attribute
print(human.temperature)
# Get the to_fahrenheit method
print(human.to_fahrenheit())
出力
37 98.60000000000001
華氏に変換する際の余分な小数点以下の桁数は、浮動小数点演算エラーによるものです。詳細については、Python 浮動小数点演算エラーをご覧ください。
temperature
のようなオブジェクト属性を割り当てたり取得したりするたびに 上記のように、Python はオブジェクトの組み込み __dict__
で検索します。 辞書属性。
>>> human.__dict__
{'temperature': 37}
したがって、man.temperature
内部的に man.__dict__['temperature']
になります .
ゲッターとセッターの使用
Celsius の使いやすさを拡張したいとします。 上記で定義されたクラス。どの物体の温度も摂氏 -273.15 度 (熱力学の絶対零度) を下回ることはできないことがわかっています
コードを更新して、この値の制約を実装しましょう。
上記の制限に対する明らかな解決策は、属性 temperature
を非表示にすることです。 (プライベートにする) 新しい getter メソッドと setter メソッドを定義して、それを操作します。これは次のように行うことができます:
# Making Getters and Setter methods
class Celsius:
def __init__(self, temperature=0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
# getter method
def get_temperature(self):
return self._temperature
# setter method
def set_temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible.")
self._temperature = value
ご覧のとおり、上記のメソッドは 2 つの新しい get_temperature()
を導入します と set_temperature()
メソッド。
さらに、temperature
_temperature
に置き換えられました .アンダースコア _
先頭の は、Python でプライベート変数を示すために使用されます。
それでは、この実装を使用しましょう:
# Making Getters and Setter methods
class Celsius:
def __init__(self, temperature=0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
# getter method
def get_temperature(self):
return self._temperature
# setter method
def set_temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible.")
self._temperature = value
# Create a new object, set_temperature() internally called by __init__
human = Celsius(37)
# Get the temperature attribute via a getter
print(human.get_temperature())
# Get the to_fahrenheit method, get_temperature() called by the method itself
print(human.to_fahrenheit())
# new constraint implementation
human.set_temperature(-300)
# Get the to_fahreheit method
print(human.to_fahrenheit())
出力
37 98.60000000000001 Traceback (most recent call last): File "<string>", line 30, in <module> File "<string>", line 16, in set_temperature ValueError: Temperature below -273.15 is not possible.
この更新により、新しい制限が正常に実装されました。摂氏 -273.15 度未満に温度を設定することはできなくなりました。
注意 :プライベート変数は実際には Python には存在しません。単に従うべき規範があります。言語自体に制限はありません。
>>> human._temperature = -300
>>> human.get_temperature()
-300
ただし、上記の更新のより大きな問題は、以前のクラスを実装したすべてのプログラムがコードを obj.temperature
から変更する必要があることです。 obj.get_temperature()
へ obj.temperature = val
のようなすべての式 obj.set_temperature(val)
まで .
このリファクタリングは、数十万行のコードを処理する際に問題を引き起こす可能性があります。
全体として、新しいアップデートには後方互換性がありませんでした。これは @property
の場所です
プロパティ クラス
上記の問題に対処する pythonic の方法は、 property
を使用することです クラス。コードを更新する方法は次のとおりです。
# using property class
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# getter
def get_temperature(self):
print("Getting value...")
return self._temperature
# setter
def set_temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible")
self._temperature = value
# creating a property object
temperature = property(get_temperature, set_temperature)
print()
を追加しました get_temperature()
内の関数 と set_temperature()
それらが実行されていることを明確に観察します。
コードの最後の行は、プロパティ オブジェクト temperature
を作成します。 .簡単に言えば、プロパティはいくつかのコードを添付します (get_temperature
と set_temperature
) メンバー属性へのアクセス (temperature
).
この更新コードを使用しましょう:
# using property class
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# getter
def get_temperature(self):
print("Getting value...")
return self._temperature
# setter
def set_temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible")
self._temperature = value
# creating a property object
temperature = property(get_temperature, set_temperature)
human = Celsius(37)
print(human.temperature)
print(human.to_fahrenheit())
human.temperature = -300
出力
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "<string>", line 31, in <module> File "<string>", line 18, in set_temperature ValueError: Temperature below -273 is not possible
ご覧のとおり、temperature
の値を取得するコードはすべて 自動的に get_temperature()
を呼び出します 辞書 (__dict__) ルックアップの代わりに。同様に、値を temperature
に割り当てるコード 自動的に set_temperature()
を呼び出します .
上記の set_temperature()
も確認できます オブジェクトを作成したときでも呼び出されました。
>>> human = Celsius(37)
Setting value...
理由はわかりますか?
その理由は、オブジェクトが作成されるとき、__init__()
メソッドが呼び出されます。このメソッドには self.temperature = temperature
という行があります .この式は自動的に set_temperature()
を呼び出します .
同様に、 c.temperature
のようなアクセス 自動的に get_temperature()
を呼び出します .これがプロパティの機能です。いくつかの例を次に示します。
>>> human.temperature
Getting value
37
>>> human.temperature = 37
Setting value
>>> c.to_fahrenheit()
Getting value
98.60000000000001
property
を使用して 、値制約の実装に変更は必要ないことがわかります。したがって、実装には下位互換性があります。
注意 :実際の温度値はプライベート _temperature
に保存されます 変数。 temperature
attribute は、このプライベート変数へのインターフェイスを提供するプロパティ オブジェクトです。
@property デコレータ
Python では、property()
property
を作成して返す組み込み関数です。 物体。この関数の構文は次のとおりです:
property(fget=None, fset=None, fdel=None, doc=None)
ここで、
fget
属性の値を取得する関数ですfset
属性の値を設定する関数ですfdel
属性を削除する関数ですdoc
文字列です (コメントなど)
実装からわかるように、これらの関数の引数はオプションです。したがって、プロパティ オブジェクトは次のように簡単に作成できます。
>>> property()
<property object at 0x0000000003239B38>
プロパティ オブジェクトには、getter()
の 3 つのメソッドがあります。 、 setter()
、および deleter()
fget
を指定する 、 fset
および fdel
後の時点で。これは次の行を意味します:
temperature = property(get_temperature,set_temperature)
次のように分類できます:
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
これら 2 つのコードは同等です。
Python デコレーターに精通しているプログラマーは、上記の構成をデコレーターとして実装できることを認識できます。
get_temperature
という名前を定義することすらできません および set_temperature
それらは不要であり、クラスの名前空間を汚染するためです。
このために、temperature
を再利用します。 ゲッター関数とセッター関数を定義する際に名前を付けます。これをデコレータとして実装する方法を見てみましょう:
# Using @property decorator
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Getting value...")
return self._temperature
@temperature.setter
def temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
# create an object
human = Celsius(37)
print(human.temperature)
print(human.to_fahrenheit())
coldest_thing = Celsius(-300)
出力
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "", line 29, in File " ", line 4, in __init__ File " ", line 18, in temperature ValueError: Temperature below -273 is not possible
上記の実装は単純で効率的です。 property
を使用することをお勧めします .
Python