Python における記述子の役割と使用法の詳細な分析

WBOY
リリース: 2016-07-06 13:29:49
オリジナル
1323 人が閲覧しました

一般的に言えば、記述子は「バインディング動作」を持つオブジェクト属性であり、そのアクセス制御は記述子プロトコル メソッドによってオーバーライドされます。これらのメソッドは、__get__()、__set__()、および __delete__() です。これらのメソッドを持つオブジェクトは記述子と呼ばれます。

属性のデフォルトのアクセス制御は、オブジェクトの辞書 (__dict__) から属性を取得 (get)、設定 (set)、および削除 (delete) します。たとえば、 a.x の検索順序は、 a.__dict__['x']、次に type(a).__dict__['x']、次に type(a) の親クラス (メタクラスを除く) を検索します。見つかったものが記述子である場合、Python は記述子のメソッドを呼び出して、デフォルトのコントロールの動作をオーバーライドします。ルックアップフェーズのどこでこのオーバーライドが行われるかは、どの記述子メソッドが定義されているかによって異なります。記述子は、新しいスタイルのクラス内でのみ機能することに注意してください。 (新しいスタイルのクラスは、型またはオブジェクトを継承するクラスです)

記述子は強力で広く使用されています。記述子は、プロパティ、インスタンス メソッド、静的メソッド、クラス メソッド、およびスーパーの背後にあるメカニズムです。記述子は、Python 2.2 で導入された新しいスタイルのクラスを実装するために、Python 自体で広く使用されています。記述子は、基礎となる C コードを簡素化し、Python での日常的なプログラミングに柔軟な新しいツール セットを提供します。

記述子プロトコル

リーリー

オブジェクトが記述子であり、オブジェクト属性として扱われる場合、デフォルトの検索動作をオーバーライドします (非常に重要)。

オブジェクトが __get__ と __set__ の両方を定義している場合、それはデータ記述子と呼ばれます。 __get__ のみを定義する記述子は、非データ記述子と呼ばれます。

データ記述子と非データ記述子の違いは次のとおりです: インスタンスのディクショナリの優先順位に関連し、インスタンス ディクショナリに記述デバイスと同じ名前の属性がある場合、記述子がデータ記述子の場合、データ記述子が最初に使用されます。非データ記述子の場合は、ディクショナリ内の属性が最初に使用されます。

リーリー

ここで B は非データ記述子であるため、a.name = 'kk' の場合、a.__dict__ に name 属性があり、それに __set__ を設定します

リーリー

データ記述子は属性へのアクセスに関してインスタンスの辞書よりも高い優先順位を持っているため、a.__dict__ は空です。

記述子の呼び出し
記述子は次のように直接呼び出すことができます: d.__get__(obj)

ただし、より一般的なケースは、プロパティにアクセスしたときに記述子が自動的に呼び出される場合です。たとえば、obj.d は obj の辞書内で d を検索します。d が __get__ メソッドを定義している場合、次の優先規則に従って d.__get__(obj) が呼び出されます。

呼び出しの詳細は、obj がクラスであるかインスタンスであるかによって異なります。さらに、記述子は、新しいスタイルのオブジェクトと新しいスタイルのクラスに対してのみ機能します。オブジェクトを継承するクラスは、新しいスタイルのクラスと呼ばれます。

オブジェクトの場合、メソッド object.__getattribute__() は b.x を type(b).__dict__['x'].__get__(b, type(b)) に変換します。具体的な実装は、この優先順位に基づいています。つまり、データ記述子はインスタンス変数よりも優先され、インスタンス変数は非データ記述子よりも優先され、__getattr__() メソッド (オブジェクトに含まれている場合) の優先順位が最も低くなります。完全な C 言語実装は、Objects/object.c の PyObject_GenericGetAttr() で確認できます。

クラスの場合、メソッド type.__getattribute__() は B.x を B.__dict__['x'].__get__(None, B) に変換します。 Python で記述すると次のようになります:

リーリー

いくつかの重要なポイント:

  • 記述子は __getattribute__() のために呼び出されます
  • __getattribute__() メソッドをオーバーライドすると、通常の記述子の呼び出しが防止されます
  • __getattribute__() は新しいスタイルのクラスのインスタンスでのみ使用できます
  • object.__getattribute__() と type.__getattribute__() は __get__() を異なる方法で呼び出します
  • データ記述子は常にインスタンス辞書より優先されます。
  • 非データ記述子は、インスタンス辞書によってオーバーライドされる場合があります。 (非データ記述子はインスタンス辞書より優先されます)
  • super() によって返されるオブジェクトには、記述子を呼び出すためのカスタマイズされた __getattribute__() メソッドもあります。 super(B, obj).m() を呼び出すと、まず obj.__class__.__mro__ で B のすぐ隣にある基底クラス A を検索し、次に A.__dict__['m'].__get__(obj, A) を返します。 )。記述子でない場合は、m が変更されずに返されます。インスタンス ディクショナリで m が見つからない場合は、バックトラックして object.__getattribute__() を呼び出して検索を続けます。 (翻訳者注: __mro__ の次の基本クラスを検索してください)

注: Python 2.2 では、m が記述子の場合、super(B, obj).m() はメソッド __get__() のみを呼び出します。 Python 2.3 では、(古いスタイルのクラスでない限り) 非データ記述子も呼び出されます。 super_getattro() の実装の詳細は、Objects/typeobject.c, [del] にあります。同等の Python 実装は、Guido のチュートリアル [/del] にあります (翻訳者注: 元の文は削除されており、参照用に保持されています)。

上記は、記述子メカニズムが object、type、および super の __getattribute__() メソッドに実装されていることを示しています。オブジェクトから派生したクラスは、このメカニズムを自動的に継承するか、同様のメカニズムを持つメタクラスを持ちます。同様に、クラスの __getattribute__() メソッドをオーバーライドして、このクラスの記述子の動作をオフにすることができます。

描述器例子
下面的代码中定义了一个资料描述器,每次 get 和 set 都会打印一条消息。重写 __getattribute__() 是另一个可以使所有属性拥有这个行为的方法。但是,描述器在监视特定属性的时候是很有用的。

class RevealAccess(object):
  """A data descriptor that sets and returns values
    normally and prints a message logging their access.
  """

  def __init__(self, initval=None, name='var'):
    self.val = initval
    self.name = name

  def __get__(self, obj, objtype):
    print 'Retrieving', self.name
    return self.val

  def __set__(self, obj, val):
    print 'Updating' , self.name
    self.val = val

>>> class MyClass(object):
  x = RevealAccess(10, 'var "x"')
  y = 5

>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5

ログイン後にコピー

这个协议非常简单,并且提供了令人激动的可能。一些用途实在是太普遍以致于它们被打包成独立的函数。像属性(property), 方法(bound和unbound method), 静态方法和类方法都是基于描述器协议的。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!