Supongamos que tenemos la siguiente jerarquía de clases:¿Hay alguna manera de acceder a __dict__ (o algo así) que incluya clases base?
class ClassA:
@property
def foo(self): return "hello"
class ClassB(ClassA):
@property
def bar(self): return "world"
Si exploro __ dict __ en ClassB como tal, sólo veo el atributo de barras:
for name,_ in ClassB.__dict__.items():
if name.startswith("__"):
continue
print(name)
salida es la barra
Puedo utilizar mis propios medios para obtener atributos no solo del tipo especificado sino de sus antepasados. Sin embargo, mi pregunta es si ya existe una forma en Python para que pueda hacer esto sin reinventar una rueda.
def return_attributes_including_inherited(type):
results = []
return_attributes_including_inherited_helper(type,results)
return results
def return_attributes_including_inherited_helper(type,attributes):
for name,attribute_as_object in type.__dict__.items():
if name.startswith("__"):
continue
attributes.append(name)
for base_type in type.__bases__:
return_attributes_including_inherited_helper(base_type,attributes)
Ejecución de mi código de la siguiente manera ...
for attribute_name in return_attributes_including_inherited(ClassB):
print(attribute_name)
... da la espalda tanto bar y foo.
Nota que estoy simplificando algunas cosas: conflictos de nombres, el uso de elementos() cuando para este ejemplo que podría utilizar dict, pasando por alto cualquier cosa que empieza con __, haciendo caso omiso de la posibilidad de que dos antepasados mismos tienen un ancestro común, etc.
EDIT1 - Intenté mantener el ejemplo simple. Pero realmente quiero tanto el nombre del atributo como la referencia del atributo para cada clase y clase antecesora. Una de las respuestas a continuación me ayuda a mejorar, publicaré un código mejor cuando lo haga funcionar.
EDIT2 - Esto hace lo que quiero y es muy breve. Se basa en la respuesta de Eli a continuación.
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
Devuelve los nombres de los atributos y sus referencias.
EDIT3 - Una de las respuestas siguientes sugiere el uso de inspect.getmembers. Esto parece muy útil porque es como Dict, pero también opera en clases de antepasados.
Dado que una gran parte de lo que yo estaba tratando de hacer era encontrar atributos marcados con un descriptor en particular, e incluyen clases antepasados, aquí hay un código que ayudaría a hacer que, en caso de que ayuda a nadie:
class MyCustomDescriptor:
# This is greatly oversimplified
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
pass
def __call__(self,decorated_function):
return self
def __get__(self,instance,type):
if not instance:
return self
return 10
class ClassA:
@property
def foo(self): return "hello"
@MyCustomDescriptor(foo="a",bar="b")
def bar(self): pass
@MyCustomDescriptor(foo="c",bar="d")
def baz(self): pass
class ClassB(ClassA):
@property
def something_we_dont_care_about(self): return "world"
@MyCustomDescriptor(foo="e",bar="f")
def blah(self): pass
# This will get attributes on the specified type (class) that are of matching_attribute_type. It just returns the attributes themselves, not their names.
def get_attributes_of_matching_type(type,matching_attribute_type):
return_value = []
for member in inspect.getmembers(type):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value.append(member_instance)
return return_value
# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor)
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type):
return_value = {}
for member in inspect.getmembers(ClassB):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value[member_name] = member_instance
return return_value
¿Qué harás exactamente con la lista de atributos una vez que la tengas? –
Las clases en cuestión representan mensajes que se han deserializado. Los mensajes usan herencia cuando comparten campos. Hay un método que necesita para imprimir información sobre cada campo de cada mensaje. Cada atributo de estas clases de mensaje está realmente marcado con un descriptor, pero dejé ese detalle ya que pensé que era irrelevante. –
... Entonces, ¿actualiza el descriptor para que agregue el atributo descrito a una lista? O, y esto podría ser un poco demasiado radical, ¿usar un dic con claves de cadena en lugar de atributos separados? –