Я начал более широко использовать протокол дескриптора 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
Редактировать Ср, 28 октября 2009 г.
Ответ Дениса дал мне соглашение об использовании в моих классах дескрипторов для получения самих объектов дескрипторов. Но у меня была целая иерархия классов дескрипторных классов, и я не хотел начинать каждую __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)