web-dev-qa-db-fra.com

Itérer sur les attributs d'objet en python

J'ai un objet python avec plusieurs attributs et méthodes. Je veux parcourir les attributs d'objet. 

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

Je souhaite générer un dictionnaire contenant tous les attributs des objets et leurs valeurs actuelles, mais je souhaite le faire de manière dynamique (donc, si par la suite j'ajoute un autre attribut, je ne dois pas oublier de mettre à jour ma fonction également).

En php, les variables peuvent être utilisées comme clés, mais les objets en python ne sont pas scriptables et si j'utilise la notation par points pour cela, cela crée un nouvel attribut avec le nom de mon var, ce qui n'est pas mon intention.

Juste pour clarifier les choses:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

Mise à jour: Par attributs, je ne parle que des variables de cet objet, pas des méthodes.

109
Pablo Mescher

En supposant que vous ayez une classe telle que

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

appeler dir sur l'objet vous redonne tous les attributs de cet objet, y compris les attributs spéciaux python. Bien que certains attributs d'objet soient appelables, tels que des méthodes.

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

Vous pouvez toujours filtrer les méthodes spéciales en utilisant une liste de compréhension.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

ou si vous préférez la carte/les filtres.

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

Si vous souhaitez filtrer les méthodes, vous pouvez utiliser la variable intégrée callable comme vérification.

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj,a))]
['bar', 'foo']

Vous pouvez également vérifier la différence entre votre classe et son parent utilisant.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])
172
Meitham

en général, mettez une méthode __iter__ dans votre class et parcourez les attributs de l'objet ou mettez cette classe mixin dans votre classe.

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

Ta classe:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}
42
SmartElectron

Les objets en python stockent leurs attributs (y compris leurs fonctions) dans un dict appelé __dict__. Vous pouvez (mais généralement pas) utiliser ceci pour accéder directement aux attributs. Si vous voulez juste une liste, vous pouvez aussi appeler dir(obj), qui renvoie un itérable avec tous les noms d'attribut, que vous pourrez ensuite transmettre à getattr.

Cependant, avoir à faire quoi que ce soit avec les noms des variables est généralement mal conçu. Pourquoi ne pas les garder dans une collection?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

Vous pouvez ensuite parcourir les clés avec for key in obj.special_values:

3
Daenyth

Comme mentionné dans certaines réponses/commentaires, les objets Python stockent déjà un dictionnaire de leurs attributs (les méthodes ne sont pas incluses). Vous pouvez y accéder en tant que __dict__, mais le meilleur moyen consiste à utiliser vars (le résultat est le même, cependant). Notez que la modification de ce dictionnaire modifiera les attributs de l'instance! Cela peut être utile, mais signifie également que vous devez faire attention à la façon dont vous utilisez ce dictionnaire. Voici un exemple rapide:

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

Utiliser dir(a) est une approche étrange, si ce n’est carrément mauvaise, de ce problème. C'est bien si vous avez vraiment besoin de parcourir tous les attributs et les méthodes de la classe (y compris les méthodes spéciales comme __init__). Cependant, cela ne semble pas être ce que vous voulez, et même la réponse acceptée réagit mal en appliquant un filtrage fragile pour essayer de supprimer les méthodes et de ne conserver que les attributs; vous pouvez voir comment cela échouerait pour la classe A définie ci-dessus.

(__dict__ a été utilisé dans quelques réponses, mais ils définissent tous des méthodes inutiles au lieu de l’utiliser directement. Seul un commentaire suggère d’utiliser vars).

2
Nathan
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

alors juste l'appeler comme un iterable

s=someclass()
for k,v in s:
    print k,"=",v
2
Joran Beasley

Pour python 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items
1
Rubber Duck

La bonne réponse à cela est que vous ne devriez pas. Si vous voulez ce genre de chose, utilisez simplement un dict, ou vous devrez explicitement ajouter des attributs à un conteneur. Vous pouvez automatiser cela en vous renseignant sur les décorateurs.

À propos, en particulier, méthode1 dans votre exemple est tout aussi bon qu'un attribut.

1
Julian

Pour python 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b
0
Rubber Duck