私が書いてきたコードでは、python記述子プロトコルをより広範囲に使用し始めました。通常、デフォルトの python ルックアップ マジックを実行したいのですが、__get__
メソッドの結果ではなく記述子オブジェクト自体を取得したい場合があります。記述子のタイプ、または記述子に格納されているアクセス状態などを知りたい。
以下のコードを書いて、正しい順序であると思われる名前空間をたどり、記述子であるかどうかに関係なく、生の属性を返します。これを行うための組み込み関数や標準ライブラリの何かが見つからないことに驚いています-そこにある必要があると思いますが、それに気づいていないか、適切な検索用語をグーグル検索していません。
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 の答えは、記述子オブジェクト自体を取得するために記述子クラスで使用する規則を教えてくれました。しかし、私は記述子クラスのクラス階層全体を持っていたので、すべて __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)