Empecé a usar el protocolo de descripción de python de forma más extensa en el código que he estado escribiendo. Normalmente, la magia de búsqueda de python predeterminada es lo que quiero que suceda, pero a veces me doy cuenta de que quiero obtener el objeto de descripción en lugar de los resultados de su método __get__
. Querer saber el tipo de descriptor, o el estado de acceso almacenado en el descriptor, o algo así.búsqueda de atributos Python sin ningún descriptor mágico?
Escribí el siguiente código para recorrer los espacios de nombres en lo que creo que es el orden correcto, y devolver el atributo en bruto independientemente de si es un descriptor o no. Sin embargo, estoy sorprendido de que no puedo encontrar una función incorporada o algo así en la biblioteca estándar para hacer esto, me imagino que tiene que estar allí y simplemente no lo he notado o busqué en Google el término de búsqueda correcto.
¿Hay alguna funcionalidad en la distribución de python que ya lo haga (o algo similar)?
Gracias!
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
Editar Miér 28 de octubre de 2009.
respuesta de Denis me dio una convención para su uso en mis clases de descriptores para obtener objetos mismos del descriptor. Sin embargo, tenía toda una jerarquía de clases de clases de descriptores, y yo no quiero empezar cada función __get__
con un texto modelo
def __get__(self, instance, instance_type):
if instance is None:
return self
...
Para evitar esto, he hecho la raíz del árbol de la clase hereda de descriptor el siguiente:
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)
¿A veces quieres el objeto descriptor? Eso viola las expectativas básicas para los descriptores: se supone que deben verse como atributos. ¿Por qué romper esa expectativa fundamental? ¿Por qué hacer esto? ¿Por qué crear algo tan complejo? –
Lo que estoy haciendo no me parece * eso * complejo, pero supongo que podría decir que estoy experimentando con el diseño. En mi caso particular actual, tengo un descriptor que devuelve la fuerza de un arma en un juego. Ese valor es una función del estado del descriptor (fuerza del arma) y la instancia (salud del barco). Hay diferentes tipos de armas; generalmente solo quiero el resultado del valor, pero en algunos casos, necesito saber qué tipo de arma es, el tipo de descriptor. ¿Y qué ocurre si un descriptor tiene métodos que no forman parte del protocolo descriptor y desea llamarlos? –