属性からプロパティまでのPython詳細な説明

文字通りの意味の違い

属性とプロパティの両方を属性に変換できます。それらの意味は中国語と英語の両方でほぼ同じですが、それでもいくつかの違いがあります。Googleを数回使用した後、より信頼できる説明を見つけました。

According to Webster, a property is a characteristic that belongs to a thing’s essential nature and may be used to describe a type or species.
An attribute is a modifier word that serves to limit, identify, particularize, describe, or supplement the meaning of the word it modifies.

簡単に言えば、プロパティはクラスの必須属性であり、カテゴリまたは種を定義および説明するために使用できます。属性は、オブジェクトの特定の属性である、説明するオブジェクトを指定するために使用されます。

例:誰もが口を持っています。人の特性の1つである大きな口を持っている人もいますが、大きな口は一部の人の属性としか言えません。

この意味で、プロパティは属性のサブセットです。

Pythonの属性とプロパティ

Pythonに戻ります。

属性とプロパティはJavaでは区別されませんが、Pythonでは異なります。以下は、Fluent Python(第19章)によって与えられた(非公式の)定義です。

次に個別に説明します。

attribute

すべてのデータ属性とメソッドは属性です。属性の所有者によると、クラス属性とインスタンス属性に分けることができます。クラスまたはインスタンスのすべての属性は、独自の__dict__属性に格納されます。

例えば:

# Python3
classFoo():
 name ='Foo class attribute'
 def fn(self):
 pass
print('class attribute:', Foo.__dict__)print()
foo =Foo()
foo.name ='foo instance attribute'print('instance attribute:', foo.__dict__)

出力:

class attribute: {‘fn’: <function Foo.fn at 0x7fd135ec8ea0 , … , ‘name’: ‘Foo class attribute’}
instance attribute: {‘name’: ‘foo instance attribute’}

property

プロパティは、セキュリティ上の理由から、データ属性をsetter / getterメソッドに置き換えることです。たとえば、読み取り専用属性や属性値の正当性の検証などです。

読み取り専用属性

例えば:

classFoo():
 def __init__(self, name):
 self.name = name

foo =Foo('I do not want to be changed')print('foo.name = ', foo.name)
foo.name ='Unluckily, I can be changed'print('foo.name = ', foo.name)

出力:

foo.name = I do not want to be changed
foo.name = Unluckily, I can be changed

上記のコードで、fooのname属性を読み取り用に外部に公開するだけで、変更したくない場合は、どうすればよいですか?読み取り専用属性のPython定義に2つの解決策がリストされています。 1つの解決策:「プライベート属性を介して」、実際には、属性の代わりにプロパティを使用することです。

**上記のfoo.nameをproperty:**に書き換えます。

classFoo():
 def __init__(self, name):
 self.__name = name

 @ property
 def name(self):return self.__name

foo =Foo('I do not want to be changed')print('foo.name = ', foo.name)
foo.name ='Luckily, I really can not be changed'

出力:

foo.name = I do not want to be changed

---------------------------------------------------------------------------
AttributeError       Traceback(most recent call last)<ipython-input-69-101c96ba497e  in<module()9 foo =Foo('I do not want to be changed')10print('foo.name = ', foo.name)---11 foo.name ='Luckily, I really can not be changed'

AttributeError: can't set attribute

注意すべき点が2つあります。

foo.nameをfoo.name =…で変更できないことは事実です。つまり、foo.nameはすでに読み取り専用属性です。

foo.nameを属性からプロパティに変更した後、アクセス方法は変更されていません。つまり、外部インターフェイスは変更されていません。この利点により、最初にプロパティと属性のどちらを使用するかを気にすることなく、落ち着いてコードを記述できます。属性は必要に応じて使用できるため、外部コードに影響を与えることなくいつでも変更できます。Javaでこれを行うことは困難です(可能な場合)。

プロパティ値の合法性の検証

上記の例では、foo.nameには読み取り専用のgetterメソッドしかありませんが、実際にはプロパティも変更可能です。setterメソッドを追加するだけで済みます。問題は、プロパティも読み取り可能で変更できるかどうかです。 、では、なぜわざわざ属性をプロパティに書き換えるのですか?

単純なショッピング関連のビジネスシナリオを想像してみてください。アイテムは、主にカテゴリ、価格、数量の属性を含め、ユーザーが購入したものを表します。

classItem():
 def __init__(self, category, count, price):
 self.cat = category
 self.count = count
 self.price = price

通常の呼び出しはこれに似ており、価格と数量は両方とも正の数値です。

item = Item(‘Bread’, 1, 10)

ただし、価格または数量が負の数値に設定されている場合、エラーは報告されません。

item.price =-10
item.count =-1
invalid_item1 =Item('Bread',-1,10)
invalid_item2 =Item('Bread',1,-10)

文法的な観点からはこれらのステートメントは合法ですが、ビジネスの観点からはすべて違法です。では、この違法な割り当てをどのように防ぐことができますか?1つの解決策は、Javaスタイルに従ってJavaスタイルを実装することです。 setterメソッドで、item.set_price(price)を使用してprice属性を設定し、set_priceメソッドで検証コードを記述します。これは実行可能ですが、Pythonicでは不十分です。Pythonスタイルは、属性名を介して読み取りと書き込みを行います。

print(item.price)
item.price = -10

これの利点は前に述べました:属性がプロパティとして書き換えられても外部インターフェイスは変更されません。したがって、item.price = -10の場合に-10の正当性を確認する方法は?最も簡単な方法は__setattr_です。 _メソッドでインターセプトを設定しますが、特に検証する必要のある属性が多数ある場合は非常に面倒です(信じられない場合は、読み取り専用属性の2番目のPython定義を参照して試してください)。

Pythonが提供する最善の解決策は、プロパティのsetterメソッドを使用することです。

classItem():
 def __init__(self, category, count, price):
 self.__cat = category # attribute
 self.count = count # property
 self.price = price # property

 @ property
 def cat(self):return self.__cat

 @ property
 def count(self):return self.__dict__['count']
 @ count.setter
 def count(self, value):if value <0:
 raise ValueError('count can not be minus: %r'%(value))
 self.__dict__['count']= value

 @ property
 def price(self):return self.__dict__['price']

 @ price.setter
 def price(self, value):if value <0:
 raise ValueError('price can not be minus: %r'%(value))
 self.__dict__['price']= value

以前は合法だったステートメントは、引き続き正常に実行できます。

item =Item('Bread',1,10)
item.price =20
item.count =2print(item.price)

ただし、次のステートメントを実行すると、エラーが報告されます。

item =Item('Bread',1,-10)
# or
item.price =-10

同じエラーが報告されます:

---------------------------------------------------------------------------
ValueError        Traceback(most recent call last)<ipython-input-93-4fcbd1284b2d  in<module()----1 item.price =-10<ipython-input-91-7546240b5469  inprice(self, value)27  def price(self, value):28if value <0:---29    raise ValueError('price can not be minus: %r'%(value))30   self.__dict__['price']= value

ValueError: price can not be minus:-10

プロパティを定義する他の方法

@ プロパティ内のプロパティは修飾子として使用できますが、実際にはクラスであるため(特定のAPIについてはドキュメントを参照してください)、上記のコードは次のように書き直すこともできます。

classItem():
 def __init__(self, category, count, price):
 self.__cat = category # attribute
 self.count = count # property
 self.price = price # property

 def get_cat(self):return self.__cat

 def get_count(self):return self.__dict__['count']

 def set_count(self, value):if value <0:
 raise ValueError('count can not be minus: %r'%(value))
 self.__dict__['count']= value

 def get_price(self):return self.__dict__['price']

 def set_price(self, value):if value <0:
 raise ValueError('price can not be minus: %r'%(value))
 self.__dict__['price']= value
 bill =property(get_bill)
 cat =property(get_cat)
 count =property(get_count, set_count)
 price =property(get_price, set_price)

この関数は要件を満たしていますが、コード自体は非常に冗長に見え、Javaのgetter / setterスタイルよりも長くなっています。現時点では、プロパティファクトリを使用してコードを簡略化できます。

まず、共有できるプロパティファクトリ関数を定義します。

def readonly_prop(storage_name):
 def getter(instance):return instance.__dict__[storage_name]returnproperty(getter)
def positive_mutable_prop(storage_name):
 def getter(instance):return instance.__dict__[storage_name]
 def setter(instance, value):if value <0:
 raise ValueError('%s can not be minus: %r'%(storage_name, value))
 instance.__dict__[storage_name]= value
 returnproperty(getter, setter)

次に、前のサンプルコードを次のように簡略化できます。

classItem():
 def __init__(self, category, count, price):
 self.__cat = category # attribute
 self.count = count # property
 self.price = price # property

 cat =readonly_prop('__cat')
 count =positive_mutable_prop('count')
 price =positive_mutable_prop('price')

このようにして、コードの単純さを保証しながら、アクセス制御と合法性の検証が実現されます。

プロパティはインスタンス属性によって上書きされません

以前、属性解析プロセスは、Pythonオブジェクトの属性アクセスプロセスの記事に示されていました。この記事から、クラス属性をインスタンス属性でオーバーライドできることがわかっています。

classFoo():
 name ='Foo'

foo =Foo()
foo.name ='foo'
codes =['Foo.name','foo.name']for code in codes:print(code,'=',eval(code))

出力は次のとおりです。

Foo.name = Foo
foo.name = foo

しかし、この種のことはプロパティには起こりません。

classFoo():
 @ property
 def name(self):return'Foo'

foo =Foo()
foo.__dict__['name']='foo'#fooを渡すことができなくなりました.割り当てられた名前
codes =['Foo.name','foo.name']for code in codes:print(code,'=',eval(code))

出力:

Foo.name = <property object at 0x7fd135e7ecc8
foo.name = Foo

少なくとも2つのことがわかります:

  1. クラスFooを介してFoo.nameにアクセスすることは、プロパティ値ではなく、プロパティオブジェクトです。

  2. foo.nameにアクセスすると、Foo.nameのプロパティ値が返されます。これは、プロパティ解決の過程で、プロパティの優先度が最も高いためです。

総括する

1. Pythonの属性はプロパティとは異なります:

attribute: data attribute + method

property: replace attribute with access control methods like getter/setter, for security reasons.

2. プロパティはさまざまな方法で定義できます。

@ property

property(getter, setter)

property factory

3. プロパティは属性の解析で最も優先度が高く、インスタンス属性によって上書きされることはありません。

上記の属性からプロパティまでのPythonの詳細な説明は、エディターが共有するすべてのコンテンツです。参考にしてください。

Recommended Posts

属性からプロパティまでのPython詳細な説明
Pythonオブジェクトの属性アクセスプロセスの詳細な説明
pythonバックトラッキングテンプレートの詳細な説明
pythonシーケンスタイプの詳細な説明
Pythonエラー処理は詳細な説明を主張します
入門から習熟までのPython(2):Pythonの概要
PythonIOポート多重化の詳細な説明
pythonコマンドの-uパラメーターの詳細な説明
Python推測アルゴリズムの問題の詳細な説明
Ubuntu20.04インストールPython3仮想環境チュートリアル詳細な説明
Python super()メソッドの原理の詳細な説明
python標準ライブラリOSモジュールの詳細な説明
詳細なチュートリアルを構築するためのPython3開発環境
01.Pythonの概要
Pythondecimalモジュールの使用法の詳細な説明
pythonがコンカレントメソッドをサポートする方法の詳細な説明
Pythonに基づくデータタイプの詳細な説明
Pythonを使用してKSを計算する詳細な例
Pythonの紹介
Centos7からCentos8にアップグレードするためのチュートリアル(詳細な図)
Python関数パラメータ分類の原理の詳細な説明
Pythonタイマースレッドプールの原理の詳細な説明
Pythonインターフェース開発の実装手順の詳細な説明
PythonWebページパーサーの使用例の詳細な説明
pythonに基づく残りの問題の詳細な説明(%)
Centos 6.4 python2.6を2.7にアップグレード
Centos 6.4 python2.6を2.7にアップグレード
詳細なPythonループのネスト
Python-モジュールの詳細な説明を要求します