2013-03-08 3 views
1

Je voudrais mettre à jour les attributs d'une classe dynamiquement, mais il semble que la combinaison de setattr et getattr ne fonctionne pas comme je voudrais l'utiliser.Python combinant setattr et getattr

Voici ma classe principale:

class Container(object): 
    def __init__(self): 
     pass 
container = Container() 
attributes = ['a', 'b', 'c', 'd'] 
values = [[1, 2, 3, 4, 5], [True, False], ['red', 'blue', 'green'], [0, 1, -1, -5, 99]] 

S'il vous plaît noter que le but de cet exemple que je construisais explicitement la liste des attributs et leurs valeurs respectives. Cependant, dans l'application réelle de ce code, je ne sais rien à l'avance. Ni leurs numéros, nom ou valeurs. Cela nécessite la nécessité de le faire de manière dynamique.

Voici le reste du code:

for key, value in zip(attributes, values): 
    setattr(container, key, []) 
    for val in value: 
     setattr(container, key, getattr(container, key).append(val)) 

cette partie ne fonctionne pas lorsque je lance le code. Je pourrais enregistrer la partie getattr dans une variable tmp puis appeler la méthode append pour la liste avant d'appeler la setattr, mais je voudrais la condenser si possible.

Quelqu'un pourrait m'expliquer pourquoi cela ne fonctionne pas? Quelle (s) alternative (s) ai-je?

Merci pour votre aide

+0

"* cette partie ne fonctionne pas lorsque j'exécute le code *" comment? Aussi, qu'attendez-vous qu'il se passe? – Blender

Répondre

3

Vous apposent à une liste en place. Comme tous dans place fonctions de mutation, .append() retours None, donc votre dernière ligne:

setattr(container, key, getattr(container, key).append(val)) 

évalue finalement:

setattr(container, key, None) 

Il suffit de définir une copie de la liste de la classe:

for key, value in zip(attributes, values): 
    setattr(container, key, values[:]) 

[:] crée une copie de values en découpant de 0 à len(values) en notation abrégée hout ayant besoin de faire une boucle.

Si tout ce que vous voulez faire est de créer un objet qui fournit les clés et les valeurs que les attributs (comme une dict serait encore avec un accès d'attribut au lieu d'un accès article), vous pouvez aussi utiliser:

class Container(object): 
    def __init__(self, names, values): 
     self.__dict__.update(zip(names, values)) 

puis exécutez:

Container(attributes, values) 

qui élimine la nécessité d'appeler setattr() dans une boucle.

+0

Je pensais qu'un 'defaultdict (liste)' était un candidat approprié ici ... –

+0

Merci pour la clarification. Savez-vous pourquoi getattr (conteneur, clé, valeur) semble fonctionner de la même manière que setattr (conteneur, clé, valeur) (une fois que l'attribut a été précédemment défini une fois)? – user1713952

+0

@ user1713952: c'est 'getattr (conteneur, clé, valeur par défaut)'; si l'attribut 'key' n'a pas été défini sur' container', alors 'default' est retourné à la place. Puisque vous avez déjà * set *, 'key',' getattr (conteneur, clé) 'suffirait. –

1

Il peut vous aider à comprendre ce qui se passe si vous pouvez suivre la progression de chaque étape:

class Container(object): 
    def __init__(self): 
     pass 
    def __str__(self): 
     return '%s(%s)' % (self.__class__.__name__, 
      ', '.join(['%s = %s' % (attr, getattr(self, attr)) 
       for attr in self.__dict__])) 

Maintenant, vous pouvez print container.Si vous faites cela dans la boucle imbriquée vous verrez qu'après la première tentative de setattr que vous avez mis à mal la liste originale stockée dans container.a (comme Martijn noté):

for key, value in zip(attributes, values): 
    setattr(container, key, []) 
    for val in value: 
     print 'before:', container 
     setattr(container, key, getattr(container, key).append(val)) 
     print 'after:', container 

before: Container(a = []) 
after: Container(a = None) 
before: Container(a = None) 
Traceback (most recent call last): ... 

La « meilleure » façon de le faire évidemment dépend du vrai problème - étant donné le problème rétréci, la version de Martijn est tout à fait appropriée, mais peut-être vous devez ajouter un élément, ou étendre avec plusieurs éléments, à un moment donné après avoir créé une instance initiale container.

Questions connexes