2009-10-27 17 views
6

私が書いたコードでは、より多くのpython記述子プロトコルを使い始めました。通常、デフォルトのPythonルックアップの魔法は私がやりたいことですが、ときどき私は__get__メソッドの結果ではなく、ディスクリプタオブジェクト自体を取得したいと思っています。記述子のタイプを知りたい、または記述子に格納されている状態にアクセスしたい、あるいは何かをしたい。記述子マジックのないpython属性の参照?

私は正しい順序であると信じている名前空間を歩き回り、記述子であるかどうかにかかわらず、属性rawを返します。私は驚いていますが、これを行うために標準ライブラリに組み込まれた関数や何かを見つけることができません - 私はそれが存在しなければならないと私は気づいていないか、または適切な検索用語のためのグーグル

すでにこれを行っているPythonディストリビューションのどこかに(または類似の)機能がありますか?

ありがとうございます!

from inspect import isdatadescriptor 

def namespaces(obj): 
    obj_dict = None 
    if hasattr(obj, '__dict__'): 
     obj_dict = object.__getattribute__(obj, '__dict__') 

    obj_class = type(obj) 
    return obj_dict, [t.__dict__ for t in obj_class.__mro__] 

def getattr_raw(obj, name): 
    # get an attribute in the same resolution order one would normally, 
    # but do not call __get__ on the attribute even if it has one 
    obj_dict, class_dicts = namespaces(obj) 

    # look for a data descriptor in class hierarchy; it takes priority over 
    # the obj's dict if it exists 
    for d in class_dicts: 
     if name in d and isdatadescriptor(d[name]): 
      return d[name] 

    # look for the attribute in the object's dictionary 
    if obj_dict and name in obj_dict: 
     return obj_dict[name] 

    # look for the attribute anywhere in the class hierarchy 
    for d in class_dicts: 
     if name in d: 
      return d[name] 

    raise AttributeError 

編集水曜日、10月28日、2009年

デニスの答えは私の記述がオブジェクト自体を得るために、私の記述子のクラスで使用するための規則を与えました。しかし、私は、ディスクリプタクラスのクラス階層全体を持っていた、と私はこれを避けるために、定型

def __get__(self, instance, instance_type): 
    if instance is None: 
     return self 
    ... 

すべての__get__機能を開始したくなかった、私はから記述子クラスツリーの継承のルートを作りました

def decorate_get(original_get): 
    def decorated_get(self, instance, instance_type): 
     if instance is None: 
      return self 
     return original_get(self, instance, instance_type) 
    return decorated_get 

class InstanceOnlyDescriptor(object): 
    """All __get__ functions are automatically wrapped with a decorator which 
    causes them to only be applied to instances. If __get__ is called on a 
    class, the decorator returns the descriptor itself, and the decorated 
    __get__ is not called. 
    """ 
    class __metaclass__(type): 
     def __new__(cls, name, bases, attrs): 
      if '__get__' in attrs: 
       attrs['__get__'] = decorate_get(attrs['__get__']) 
      return type.__new__(cls, name, bases, attrs) 
+0

時には、ディスクリプタオブジェクトが必要ですか?これはディスクリプタの中核的な期待に反するものです。ディスクリプタは属性のように見えます。なぜその基本的な期待を破るのですか?なぜこれを行うのですか?なぜ複雑なものを作るのですか? –

+0

私がやっていることは、私にとっては複雑な感じではありませんが、私はそのデザインを実験していると言えるでしょう。 私の現在の特定のケースでは、私はゲーム内の武器の強さを返す記述子を持っています。その値は、記述子の状態(武器の強さ)とインスタンス(船の健康状態)の関数です。さまざまな種類の武器があります。通常は値の結果がほしいですが、いくつかのケースでは、どのような種類の兵器であるかを知る必要があります。記述子のタイプです。 そして、記述子がその記述子プロトコルの一部ではないメソッドを持っていて、それらを呼び出す場合はどうなりますか? –

答えて

11

ほとんどのディスクリプタは、インスタンス属性としてのみアクセスされるとジョブを実行します。だから、それはクラスにアクセスしていますとき、それは自分自身を返すために便利です:

class FixedValueProperty(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, inst, cls): 
     if inst is None: 
      return self 
     return self.value 

これは、あなたが記述自体を取得することができます:これはのために働くことを

>>> class C(object): 
...  prop = FixedValueProperty('abc') 
... 
>>> o = C() 
>>> o.prop 
'abc' 
>>> C.prop 
<__main__.FixedValueProperty object at 0xb7eb290c> 
>>> C.prop.value 
'abc' 
>>> type(o).prop.value 
'abc' 

注、(ほとんど?)内蔵の記述子すぎ:あなたは、サブクラス内のエクステントそれをする必要がある場合の記述子へのアクセス

>>> class C(object): 
...  @property 
...  def prop(self): 
...   return 'abc' 
... 
>>> C.prop 
<property object at 0xb7eb0b6c> 
>>> C.prop.fget 
<function prop at 0xb7ea36f4> 

は役に立つかもしれませんが、これを行うにはbetter wayがあります。

+0

それは分かりませんでした。知っておいてよかった。関数そのものはそのパターンの例外ではなく、唯一の例外かもしれません。組み込み記述子を突き刺さなければならないでしょう。 –

+0

正確に尋ねられたように私の質問に答えなかったけれども、あなたの答えは根本的な問題を解決するのに役立ちました。私はそれを受け入れるでしょう。 –

+0

はこのパターンの例外です(私はあなたが方法について話すと仮定します)?いいえ、 'c.method'は記述からバインドされたメソッドを返しますが、' C.method'はアンバインドされたメソッドを返します。それは同じパターンです。 – u0b34a0f6ae

0

上記の方法で、プロパティのコードを制御するたび

class FixedValueProperty(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, inst, cls): 
     if inst is None: 
      return self 
     return self.value 

は素晴らしい方法ですが、そのようなプロパティは、他の誰かによって制御されるライブラリの一部である場合など、いくつかの例、他がありますアプローチが有用です。この代替アプローチは、オブジェクトマッピングの実装、質問に記載された名前空間の歩行、または他の特殊なライブラリなどの他の状況でも役立ちます。

は、単純なプロパティを持つクラスを考えてみましょう:コンテナオブジェクトクラス辞書からアクセスする場合

class ClassWithProp: 

    @property 
    def value(self): 
     return 3 
>>>test=ClassWithProp() 
>>>test.value 
3 
>>>test.__class__.__dict__.['value'] 
<property object at 0x00000216A39D0778> 

、「記述子のマジックを」バイパスされます。また、新しいクラス変数にプロパティを割り当てると、 'descriptor magic'を持つオリジナルのように動作しますが、インスタンス変数に代入された場合、プロパティは通常のオブジェクトとして動作し、 'descriptor magic'もバイパスします。 inspect.getattr_static

>>> test.__class__.classvar = test.__class__.__dict__['value'] 
>>> test.classvar 
3 
>>> test.instvar = test.__class__.__dict__['value'] 
>>> test.instvar 
<property object at 0x00000216A39D0778> 
3

inspectライブラリはどんな記述子魔法なしの属性を取得するための機能を提供します。

ドキュメント:https://docs.python.org/3/library/inspect.html#fetching-attributes-statically

(これは古い質問ですが、私はこれを行う方法を覚えしようとすると、それを越えてくるので、私は再びそれを見つけることができますので、私はこの答えを投稿しています!)

0

の記述子を取得したいとしましょう。ここではtype(obj) is Cです。 C(つまり、Cにバインド)でアクセスすると、通常、ディスクリプタ自体が返されるため、通常は機能します。しかし、C.propはメタクラスに記述子をトリガーすることがあります。 propobjに存在しない場合、obj.propAttributeErrorを発生し、C.propは発生しない可能性があります。したがって、inspect.getattr_static(obj, 'prop')を使用する方が良いです。

あなたはそれに満足していない場合は、ここで(Objects/object.c_PyObject_GenericGetAttrWithDictから)CPythonの固有の方法があります:

import ctypes, _ctypes 

_PyType_Lookup = ctypes.pythonapi._PyType_Lookup 
_PyType_Lookup.argtypes = (ctypes.py_object, ctypes.py_object) 
_PyType_Lookup.restype = ctypes.c_void_p 

def type_lookup(ty, name): 
    """look for a name through the MRO of a type.""" 
    if not isinstance(ty, type): 
     raise TypeError('ty must be a type') 

    result = _PyType_Lookup(ty, name) 
    if result is None: 
     raise AttributeError(name) 

    return _ctypes.PyObj_FromPtr(result) 

type_lookup(type(obj), 'prop')objがある場合はCPythonはobj.propでそれを使用するのと同じ方法で記述子を返します。通常のオブジェクト(クラスなどではない)。

関連する問題