14 votes

Existe-t-il un moyen d'accéder à __dict__ (ou quelque chose de similaire) qui inclut les classes de base ?

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X