Pythonのpropertyプロパティ

6997 ワード

Pythonにはpropertyという素晴らしい概念があり、オブジェクト向けのプログラミングをより簡単にします.Pythonのpropertyを詳しく説明し、深く理解する前に、なぜpropertyを使う必要があるのかという直感をまず確立しましょう.
1つのインスタンスから開始
ある日、摂氏温度を格納するクラスを作成することにしたとします.もちろん、このクラスは摂氏温度を華氏温度に変換する方法を実現する必要がある.1つの実装方法は次のとおりです.
Python
 
1 2 3 4 5
class Celsius:     def __init__(self, temperature = 0):         self.temperature = temperature     def to_fahrenheit(self):         return (self.temperature * 1.8) + 32
このクラスでオブジェクトを生成し、オブジェクトの温度プロパティを所望の方法で変更できます.
Python
 
1 2 3 4 5 6 7 8 9 10 11 12 13
>>> # create new object >>> man = Celsius()   >>> # set temperature >>> man.temperature = 37   >>> # get temperature >>> man.temperature 37   >>> # get degrees Fahrenheit >>> man.to_fahrenheit() 98.60000000000001
ここで追加の小数点部分は、華氏温度に変換したときの浮動小数点演算誤差によるものです(Python解釈器で1.1+2.2を試してみてください).オブジェクトのプロパティを割り当てたり取得したりするたびに、上記の温度など、Pythonはオブジェクトの__dict__辞書から検索します.
Python
 
1 2
>>> man.__dict__ {'temperature': 37}
だから、man.temperatureはその内部でman.__dict__['temperature']になりました
次に、私たちのクラスがお客様に人気があると仮定し、プログラムでこのクラスを使用し始めました.彼らはこのクラスで生成されたオブジェクトに対して様々な操作を行った.ある日、信頼されているお客様が私たちに会いに来て、温度が-273度を下回ってはいけないことを提案しました(熱力学の同級生は異議を申し立てるかもしれませんが、実際には-273.15です)、絶対ゼロとも呼ばれています.お客様は、この値制約をさらに要求しています.お客様の満足度を勝ち取ることを自分の責任とする会社として、私たちは喜んでアドバイスに従って、1.01バージョンを発表して、私たちの既存のクラスをアップグレードしました.
GettersとSettersの使用
上記のコンストレイントでは、温度プロパティを非表示にし(プライベート化)、温度プロパティを操作するためのgetterインタフェースとsetterインタフェースを定義することが容易に考えられます.次のように実現できます.
Python
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Celsius:     def __init__(self, temperature = 0):         self.set_temperature(temperature)       def to_fahrenheit(self):         return (self.get_temperature() * 1.8) + 32       # new update     def get_temperature(self):         return self._temperature       def set_temperature(self, value):         if value < -273:             raise ValueError("Temperature below -273 is not possible")         self._temperature = value
以上から,2つの新しい方法get_temperature()set_temperature()を定義し,さらに属性temperatureも_temperatureに置き換えた.一番前の下線()Pythonのプライベート変数を示すために使用します.
Python
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14
>>> c = Celsius(-277) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible   >>> c = Celsius(37) >>> c.get_temperature() 37 >>> c.set_temperature(10)   >>> c.set_temperature(-300) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
この更新は新しい制約を実現することに成功し,−273度未満の温度設定を許可しなくなった.
Pythonには実際にはプライベート変数はありません.いくつかの簡単な規範があります.Python自体には制限は適用されません.
Python
 
1 2 3
>>> c._temperature = -300 >>> c.get_temperature() -300
しかし、これでは安心できません.上記の更新の最大の問題は、彼らのプログラムで以前のクラスを使用したすべてのお客様がコードを変更する必要があることです:obj.temperatureをobjに変更する.get_temperature()では、objなどのすべての付与文も変更する必要があります.temperature=valをobjに変更する.set_temperature(val).このような再構築は、数千行のコードを持つお客様に大きな迷惑をかけます.
要するに、私たちの更新は後方互換性がありません.これがpropertyのキラキラした登場が必要な場所です.
プロパティの役割
上記の問題に対してPython式の解決策はpropertyを用いることである.ここでは、すでに実装されているバージョンです.
Python
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
class Celsius:     def __init__(self, temperature = 0):         self.temperature = temperature       def to_fahrenheit(self):         return (self.temperature * 1.8) + 32       def get_temperature(self):         print("Getting value")         return self._temperature       def set_temperature(self, value):         if value < -273:             raise ValueError("Temperature below -273 is not possible")         print("Setting value")         self._temperature = value       temperature = property(get_temperature,set_temperature) get_temperature()set_temperature()の内部にprint()関数を追加し,それらが実行されているかどうかを明確に観察した.コードの最後の行にpropertyオブジェクトtemperatureが作成されます.簡単に言えば、propertyは、メンバー属性(temperature)のアクセスエントリにいくつかのコード(get_temperatureおよびset_temperature)を付加する.temperature値を取得したコードは、辞書テーブル(get_temperature())で検索するのではなく、__dict__を自動的に呼び出します.同様に、temperature値に割り当てられたコードもset_temperature()を自動的に呼び出す.これはPythonのクールな機能です.実際に実証してみましょう.
Python
 
1 2
>>> c = Celsius() Setting value
上のコードから、オブジェクトを作成するとset_temperature()が呼び出されることがわかります.なぜか推測できますか?なぜなら、1つのオブジェクトが作成されると、__init__()メソッドが呼び出されるからである.この方法には1行のコードselfがある.temperature = temperature.このタスクは、set_temperature()メソッドを自動的に呼び出します.
Python
 
1 2 3
>>> c.temperature Getting value 0
同様に、c.temperatureなどの属性へのアクセスについても、get_temperature()メソッドが自動的に呼び出されます.これがpropertyのしたことです.ここには追加の例があります.
Python
 
1 2 3 4 5 6
>>> c.temperature = 37 Setting value   >>> c.to_fahrenheit() Getting value 98.60000000000001
propertyを使用することで、お客様のコードを変更する必要がなく、クラスを変更し、値制約を実現していることがわかります.そのため、私たちの実現は後方互換性があり、このような結果、みんな喜んでいます.
最後に、実際の温度値は、プライベート変数_temperatureに格納されることに留意されたい.プロパティtemperatureはpropertyオブジェクトであり、このプライベート変数にインタフェースを提供するために使用されます.
propertyを深く掘り起こす
Pythonでは、property()はpropertyオブジェクトを作成して返すための組み込み関数です.この関数の署名は次のとおりです.
Python
 
1
property(fget=None, fset=None, fdel=None, doc=None)
ここで、fgetは属性値を取得する関数であり、fsetは属性値を設定する関数であり、fdelは属性を削除する関数であり、docは文字列(注釈に類似)である.これらの関数パラメータは、関数実装から選択可能です.したがって、propertyオブジェクトは、次のように簡単に作成できます.
Python
 
1 2
>>> property()
Propertyオブジェクトには、オブジェクト作成後にfget、fset、fdelを設定する3つの方法があります.つまり、この行のコード:temperature=property(get_temperature,set_temperature)は、次のように分解できます.
Python
 
1 2 3 4 5 6
# make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature)
それらの間は互いに等価である.
Pythonの装飾器decoratorに詳しいプログラマーは、上記の構造がdecoratorとして実現できることを認識することができる.名前getを定義しないでtemperatureとset_temperatureは、必要ではなく、クラスのネーミングスペースを汚染しているためです.このため、getter関数とsetter関数を定義するときにtemperatureという名前を再利用します.下のコードは、それを実装する方法を示します.
Python
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
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):         if value < -273:             raise ValueError("Temperature below -273 is not possible")         print("Setting value")         self._temperature = value
上記の2つの生成propertyの実現方式は、いずれも簡単で、推奨されています.Pythonがpropertyを探している間に、このようなコード構造に遭遇する可能性があります.