403 votes

Dictionnaire Python à partir des champs d'un objet

Savez-vous s'il existe une fonction intégrée permettant de construire un dictionnaire à partir d'un objet arbitraire ? J'aimerais faire quelque chose comme ceci :

>>> class Foo:
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> props(f)
{ 'bar' : 'hello', 'baz' : 'world' }

NOTE : Il ne doit pas inclure de méthodes. Seulement des champs.

511voto

user6868 Points 1796

Notez que la meilleure pratique dans Python 2.7 est d'utiliser nouveau style (non nécessaire avec Python 3), c'est-à-dire

class Foo(object):
   ...

De plus, il y a une différence entre un "objet" et une "classe". Pour construire un dictionnaire à partir d'un objet arbitraire objet il suffit d'utiliser __dict__ . En général, vous déclarez vos méthodes au niveau de la classe et vos attributs au niveau de l'instance, donc __dict__ devrait convenir. Par exemple :

>>> class A(object):
...   def __init__(self):
...     self.b = 1
...     self.c = 2
...   def do_nothing(self):
...     pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}

Une meilleure approche (suggérée par robert dans les commentaires) est la fonction intégrée vars fonction :

>>> vars(a)
{'c': 2, 'b': 1}

Alternativement, en fonction de ce que vous voulez faire, il pourrait être agréable d'hériter de dict . Alors votre classe est déjà un dictionnaire, et si vous voulez vous pouvez remplacer getattr et/ou setattr pour appeler et régler la dictée. Par exemple :

class Foo(dict):
    def __init__(self):
        pass
    def __getattr__(self, attr):
        return self[attr]

    # etc...

3 votes

Que se passe-t-il si l'un des attributs de A a un getter personnalisé ? (une fonction avec un décorateur @property) ? Apparaît-il toujours dans ____dict____ ? Quelle sera sa valeur ?

12 votes

__dict__ ne fonctionnera pas si l'objet utilise des slots (ou est défini dans un module C).

1 votes

Existe-t-il un équivalent de cette méthode pour les objets de la classe ? Par exemple, au lieu d'utiliser f=Foo() puis de faire f.__dict__, faites directement Foo.__dict__ ?

63voto

dF. Points 29787

El dir vous donnera tous les attributs de l'objet, y compris les méthodes spéciales telles que __str__ , __dict__ et tout un tas d'autres dont vous ne voulez probablement pas. Mais vous pouvez faire quelque chose comme :

>>> class Foo(object):
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> [name for name in dir(f) if not name.startswith('__')]
[ 'bar', 'baz' ]
>>> dict((name, getattr(f, name)) for name in dir(f) if not name.startswith('__')) 
{ 'bar': 'hello', 'baz': 'world' }

Vous pouvez donc l'étendre pour ne retourner que les attributs de données et non les méthodes, en définissant votre props comme ceci :

import inspect

def props(obj):
    pr = {}
    for name in dir(obj):
        value = getattr(obj, name)
        if not name.startswith('__') and not inspect.ismethod(value):
            pr[name] = value
    return pr

1 votes

Ce code inclut des méthodes. Existe-t-il un moyen d'exclure les méthodes ? Je n'ai besoin que des champs de l'objet. Merci

1 votes

ismethod n'attrape pas les fonctions. Exemple : inspect.ismethod(str.upper) . inspect.isfunction n'est pas beaucoup plus utile, cependant. Je ne sais pas comment aborder ça tout de suite.

0 votes

J'ai fait quelques ajustements pour récurer grossièrement et ignorer toutes les erreurs à une profondeur ici, merci ! gist.github.com/thorsummoner/bf0142fd24974a0ced778768a33a306‌​9

30voto

Julio César Points 3007

J'ai opté pour une combinaison des deux réponses :

dict((key, value) for key, value in f.__dict__.iteritems() 
    if not callable(value) and not key.startswith('__'))

0 votes

Cela fonctionne aussi, mais attention, cela ne vous donnera que les attributs définis sur l'instance, pas sur la classe (comme la classe Foo dans votre exemple)...

0 votes

Donc, jcarrascal, il est préférable d'envelopper le code ci-dessus dans une fonction comme props(), puis vous pouvez appeler soit props(f) soit props(Foo). Notez qu'il est presque toujours préférable d'écrire une fonction plutôt que du code "en ligne".

0 votes

Bien, notez que ceci est pour python2.7, pour python3, remplacez iteritems() par simplement items().

15voto

indentation Points 2178

Pour construire un dictionnaire à partir d'un objet il suffit d'utiliser __dict__ .

Il manque les attributs que l'objet hérite de sa classe. Par exemple,

class c(object):
    x = 3
a = c()

hasattr(a, 'x') est vrai, mais 'x' n'apparaît pas dans a.__dict__

0 votes

Dans ce cas, quelle est la solution ? Puisque vars() ne fonctionne pas

0 votes

@should_be_working dir est la solution dans ce cas. Voir l'autre réponse à ce sujet.

9voto

Score_Under Points 336

Réponse tardive mais fournie par souci d'exhaustivité et pour le bénéfice des googleurs :

def props(x):
    return dict((key, getattr(x, key)) for key in dir(x) if key not in dir(x.__class__))

Cela ne montrera pas les méthodes définies dans la classe, mais cela montrera quand même les champs, y compris ceux assignés aux lambdas ou ceux qui commencent par un double soulignement.

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