質問

私が書いているコードでは、Python記述子プロトコルをより広範囲に使用し始めました。通常、デフォルトのPythonルックアップマジックは実現したいことですが、時々 __ get __ メソッドの結果ではなく、記述子オブジェクト自体を取得したいことがあります。記述子のタイプ、記述子に格納されているアクセス状態、またはそのようなことを知りたい。

正しい順序であると思われる名前空間を調べ、記述子であるかどうかに関係なく、属性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

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

Denisの答えは、記述子オブジェクト自体を取得するために記述子クラスで使用する規則を与えてくれました。しかし、記述子クラスのクラス階層全体があり、ボイラープレートで every __ get __ 関数を開始したくありませんでした

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

これを回避するために、記述子クラスツリーのルートに次を継承させました:

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)
役に立ちましたか?

解決

ほとんどの記述子は、インスタンス属性としてのみアクセスされたときに機能します。したがって、クラスにアクセスしたときに自分自身を返すのが便利です:

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>

サブクラスでエクステントを拡張する必要がある場合、ディスクリプタにアクセスすると便利ですが、これを行うより良い方法

他のヒント

inspect ライブラリは、記述子マジックなしで属性を取得する関数を提供します: inspect.getattr_static

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

(これは古い質問ですが、これを行う方法を思い出そうとすると出くわすので、この答えを投稿しますので、また見つけられるようになります!)

上記の方法

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>

コンテナオブジェクトクラス dict からアクセスすると、「ディスクリプタマジック」はバイパスされます。また、プロパティを新しいクラス変数に割り当てると、「ディスクリプタマジック」を持つ元のプロパティと同じように動作しますが、インスタンス変数に割り当てると、プロパティは通常のオブジェクトとして動作し、「ディスクリプタマジック」もバイパスします。 / p>

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

type(obj)がC である obj.prop の記述子を取得したいとしましょう。

C.prop は通常、 C を介してアクセスされたときに(つまり、 C にバインドされたときに)記述子がそれ自体を返すため、正常に機能します。ただし、 C.prop はそのメタクラスで記述子をトリガーする場合があります。 prop obj に存在しない場合、 obj.prop C.prop はそうではないかもしれません。したがって、 inspect.getattr_static(obj、 'prop')を使用することをお勧めします。

これに満足できない場合、CPython固有のメソッドを次に示します( Objects / object.c _PyObject_GenericGetAttrWithDict から):

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')は、CPythonが obj.prop if obj は通常のオブジェクトです(たとえば、クラスではありません)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top