PEP(Python Enhancement Proposal)はPythonを改良する案、リリーススケジュール、コーディングスタイルなど開発を円滑に進める上で重要なことを文書にまとめておく場所。ここには、Pythonをコーディングするすべての人にとって重要な内容がきっと入っているはず。ビギナーが言うようにもちろん、英語。よって、これをできるだけ毎日、翻訳しながら読み解きまとめていくことで備忘録としていきたいと思います。英語は非常に苦手なので誤訳はご指摘ください。
※以下、アイコンが出てくる箇所は私のコメントになります。それ以外は、翻訳または翻訳のまとめです。
Contents
基本情報
Attribute Access Handlers
PEP: | 213 | |
---|---|---|
Title: | Attribute Access Handlers | |
属性アクセスハンドラ | ||
Author: | paul at prescod.net (Paul Prescod) | |
Status: | Deferred | |
Type: | Standards Track | |
Created: | 21-Jul-2000 | |
Python-Version: | 2.1 | |
Post-History: |
イントロ
インスタンスのクライアントコードが属性を設定して代わりにコードを実行しようとしたときにPythonコードや拡張モジュールで「trap
」することは可能であり比較的一般的です。基になる実装が直接バインディングを変更するのではなく何らかの計算を行っている場合でも、ユーザーが属性の割り当て/取得/削除の構文を使用できるようにすることができます。
このPEPは、Pythonインスタンス用にこれらのハンドラを実装することをより簡単に、より効率的に、そしてより安全にする機能を説明しています。
正当化
シナリオ1
“stdout
“という名前の属性を処理するデプロイ済みクラスがあります。割り当ての時点でstdout
が本当に “write
“メソッドを持つオブジェクトであることを確認するのが良い。setstdout
メソッド(デプロイされたコードと互換性がない)に変更するのではなく、trap
してオブジェクトの型を確認します。
シナリオ2
属性割り当ての概念を持つオブジェクトモデルとできるだけ互換性があるべきです。例えばW3C Document Object Model
または特定のCOM
インターフェース(例えば、PowerPointインターフェース)でそうあるべきです。基礎となる実装では属性をまったく使用しなくても、モデル内の属性をPythonインタフェースの属性として表示することを推奨します。
シナリオ3
ユーザーが属性を読み取り専用にしたい。
この機能により、プログラマは目的を問わず、基礎となる実装から自分のモジュールのインタフェースを切り離すことができます。繰り返しますが、これは新しい機能ではなく、単に既存の規約の新しい構文です。
現在のソリューション
一部の属性を読み取り専用にするには
class foo: def __setattr__( self, name, val ): if name=="readonlyattr": raise TypeError elif name=="readonlyattr2": raise TypeError ... else: self.__dict__["name"]=val
これには以下の問題があります。
__setattr__
のどこか他の場所で特定の目的のためにtrap
されているかどうかを熟知している必要があります。辞書に割り当てるのではなく、そのメソッドを呼び出さなければなりません。__setattr__
をオーバーロードするにはさまざまな理由があるため、衝突の可能性はかなりあります。例えば、オブジェクトデータベースの実装は、全く関係のない目的のためにsetattr
をオーバーロードすることがよくあります。switch
文は、すべての属性ハンドラをコード内の1か所に指定することを強制しています。タスク固有のメソッド(モジュール化のため)にディスパッチすることがありますが、これはパフォーマンス上の問題を引き起こす可能性があります。__getattr__
、__setattr__
、__delattr__
でなければなりません。これは追加レベルのメソッド呼び出しによって軽減できますが、これは非効率的です。提案されている構文
特別なメソッドは、次の形式の宣言で自分自身を宣言する必要があります。
class x: def __attr_XXX__(self, op, val ): if op=="get": return someComputedValue(self.internal) elif op=="set": self.internal=someComputedValue(val) elif op=="del": del self.internal
クライアント側のコードはこのようになります。
fooval=x.foo x.foo=fooval+5 del x.foo
セマンティクス
3種類すべての属性参照でこのメソッドを呼び出す必要があります。opパラメータは “get
” / “set
” / “del
“です。この文字列はインターンされるので、文字列の実際のチェックは非常に高速になります。
実際には__attr_XXX__
という名前のメソッドと同じインスタンス内にXXX
という名前の属性を持つことはできません。
適切な属性が見つからなかった場合にのみ__getattr__
が呼び出されることになっているという原則に基づいて、__attr_XXX__
の実装は__getattr__
の実装よりも優先されます。
__attr_XXX__
の実装は、一貫性を保つために__setattr__
の実装よりも優先されます。反対もまた実行可能であるように思われます。 __del_y__
についても同じことが言えます。
提案された実装
属性アクセスハンドラと呼ばれる新しいオブジェクトタイプがあります。このタイプのオブジェクトには、以下の属性があります。
name (e.g. XXX, not __attr__XXX__) method (pointer to a method object)
PyClass_Newでは、適切な形式のメソッドが検出され、オブジェクトに変換されます(非バインドメソッドオブジェクトとまったく同じです)。これらはXXX
という名前でクラス__dict__
に格納されています。元のメソッドは、元の名前で非バインドメソッドとして保存されます。
インスタンス内に属性アクセスハンドラがまったく存在しない場合は、フラグが設定されます。それを一旦 “I_have_computed_attributes
“と呼びます。派生クラスは基本クラスからフラグを継承します。インスタンスはクラスからフラグを継承します。
オブジェクトが返される直前まで、get
は通常どおり進行します。返されたオブジェクトがメソッドであるかどうかの現在のチェックに加えて、返されたオブジェクトがアクセスハンドラであるかどうかもチェックします。もしそうなら、それはgetter
メソッドを呼び出して値を返すでしょう。属性アクセスハンドラを削除するには、辞書を直接操作します。
セットは “I_have_computed_attributes
“フラグをチェックすることによって進行します。設定されていない場合は、いままでと同じように進みます。設定されている場合は、要求されたオブジェクト名を辞書で取得する必要があります。属性アクセスハンドラを返す場合は、その値を使ってsetter
関数を呼び出します。他のオブジェクトが返された場合は、結果を破棄していままでと同じように続行します。属性アクセスハンドラを持つことは、特定のインスタンス上のすべてのセットの属性 “setting
“パフォーマンスに多少影響を及ぼしますが、__setattr__
を使用することいままでよりも影響は少なくなります。gets
は前の__getattr__
よりも効率的です。
I_have_computed_attributes
フラグは、この機能を使用していないオブジェクトに対して、 “set
“ごとに余分な “get
“が発生することによるパフォーマンスの低下を防ぐためのものです。このフラグをチェックすると、すべてのオブジェクトに対してパフォーマンス上の影響がわずかに発生します。
delete
の実装はset
の実装と同様です。
注意事項
I_have_computed_attributes
フラグを最新に保つロジックを提案していないことにお気づきかもしれません。これは現在のPythonとのままです。使用中のオブジェクトに__setattr__
メソッドを追加した場合、そのメソッドは「コンパイル」時に使用可能であったようには動作しません。ダイナミズムは、追加の実装作業に見合う価値はありません。このスニペットは現在の動作を示しています。>>> def prn(*args):print args >>> class a: ... __setattr__=prn >>> a().foo=5 (<__main__.a instance at 882890>, 'foo', 5) >>> class b: pass >>> bi=b() >>> bi.__setattr__=prn >>> b.foo=5
__dict __["XXX"]
への代入は、__attr_XXX__
の属性アクセスハンドラを上書きする可能性があります。アクセスハンドラは情報をプライベートな__XXX
変数に格納します。setattr
またはgetattr
を呼び出そうとする属性アクセスハンドラは、無限ループを引き起こす可能性があります(__getattr__
と同様)。__XXXなど
の特別な(通常はプライベートな)変数を使用することが解決策です。特記
PEP252に記述されている記述子メカニズムは、より直接的にサポートするのに十分強力です。これを可能にするために ‘getset
‘コンストラクタを言語に追加するという手段があります。
class C: def get_x(self): return self.__x def set_x(self, v): self.__x = v x = getset(get_x, set_x)
構文を追加するか、命名規則を認識することができます。