Supposons que nous ayons la hiérarchie de classes suivante :
class ClassA:
@property
def foo(self): return "hello"
class ClassB(ClassA):
@property
def bar(self): return "world"
Si j'explore __ dict __ sur ClassB comme ça, je ne vois que l'attribut bar :
for name,_ in ClassB.__dict__.items():
if name.startswith("__"):
continue
print(name)
La sortie est une barre
Je peux mettre en place mes propres moyens pour obtenir des attributs non seulement sur le type spécifié mais aussi sur ses ancêtres. Cependant, ma question est de savoir s'il existe déjà un moyen dans Python de le faire sans réinventer la roue.
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)
J'exécute mon code comme suit...
for attribute_name in return_attributes_including_inherited(ClassB):
print(attribute_name)
... rend à la fois bar et foo.
Notez que je simplifie certaines choses : collisions de noms, utilisation de items() alors que pour cet exemple je pourrais utiliser dict, sauter tout ce qui commence par __, ignorer la possibilité que deux ancêtres aient eux-mêmes un ancêtre commun, etc.
EDIT1 - J'ai essayé de garder l'exemple simple. Mais je veux vraiment à la fois le nom de l'attribut et la référence de l'attribut pour chaque classe et classe ancêtre. L'une des réponses ci-dessous m'a mis sur une meilleure voie, je posterai un meilleur code lorsque je l'aurai fait fonctionner.
EDIT2 - Ceci fait ce que je veux et est très succinct. Elle est basée sur la réponse d'Eli ci-dessous.
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
Il restitue à la fois les noms des attributs et leurs références.
EDIT3 - Une des réponses ci-dessous suggère d'utiliser inspect.getmembers. Cela semble très utile car c'est comme dict, sauf que cela fonctionne aussi sur les classes ancêtres.
Puisqu'une grande partie de ce que j'essayais de faire était de trouver des attributs marqués avec un descripteur particulier, et d'inclure les classes d'ancêtres, voici un peu de code qui aiderait à faire cela au cas où cela aiderait quelqu'un :
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