web-dev-qa-db-fra.com

Comment créer un objet et y ajouter des attributs?

Je veux créer un objet dynamique (à l'intérieur d'un autre objet) en Python, puis y ajouter des attributs.

J'ai essayé:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

mais cela n'a pas fonctionné.

Des idées?

modifier:

Je configure les attributs à partir d'une boucle for qui parcourt une liste de valeurs, par exemple.

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

Dans l'exemple ci-dessus, j'obtiendrais obj.a.attr1, obj.a.attr2, obj.a.attr3

J'ai utilisé la fonction setattr parce que je ne savais pas comment faire obj.a.NAME à partir d'une boucle for.

Comment définir l'attribut en fonction de la valeur de p dans l'exemple ci-dessus?

238
John

Vous pouvez utiliser mon ancien Bunch recette, mais si vous ne voulez pas créer de "classe de groupe", une très simple existe déjà en Python - toutes les fonctions peuvent avoir des attributs arbitraires (y compris les fonctions lambda). Donc, les travaux suivants:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

Que la perte de clarté par rapport à la vénérable recette Bunch soit correct, est une décision de style que je laisserai bien sûr à vous de décider.

176
Alex Martelli

La variable object intégrée peut être instanciée mais aucun attribut ne peut y être défini. (J'aimerais bien pouvoir le faire, dans ce but précis.) Il n'a pas de __dict__ pour contenir les attributs.

Je fais généralement juste ceci:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Lorsque je peux, je donne à la classe Object un nom plus significatif, en fonction du type de données que je mets.

Certaines personnes font la même chose en utilisant une sous-classe de dict qui permet aux attributs d'accéder aux clés. (d.key au lieu de d['key'])

Modifier : Pour l’ajout à votre question, utiliser setattr convient. Vous ne pouvez simplement pas utiliser setattr sur les instances object().

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)
278
FogleBird

Il y a types.SimpleNamespace class dans Python 3.3+ :

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtuple , typing.NamedTuple pourrait être utilisé pour des objets immuables. PEP 557 - Classes de données suggère une alternative mutable.

Pour une fonctionnalité plus riche, vous pouvez essayer le paquet attrs . Voir un exemple d'utilisation .

95
jfs

Il y a plusieurs façons d’atteindre cet objectif… .. Vous avez essentiellement besoin d’un objet extensible.

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()
30
evilpie

Maintenant, vous pouvez faire (pas sûr si c'est la même réponse que evilpie):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
16
andreabedini

Le module mock est fondamentalement conçu pour cela.

import mock
obj = mock.Mock()
obj.a = 5
15
Dunatotatos

Essayez le code ci-dessous:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']
10
neldor

comme docs disent :

Note: object ne pas a un __dict__, vous ne pouvez donc pas attribuer d'attributs arbitraires à une instance de la classe object.

Vous pouvez simplement utiliser une instance factice.

7
SilentGhost

Vous pouvez également utiliser directement un objet de classe. cela crée un espace de noms:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
7
Ernesto

Ces solutions sont très utiles lors des tests. En me basant sur les réponses de tous les autres, je le fais dans Python 2.7.9 (sans méthode statique, j'obtiens un TypeError (méthode non liée ...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4
2
Robpol86

Si nous pouvons déterminer et regrouper tous les attributs et toutes les valeurs avant de créer l'objet imbriqué, nous pourrions créer une nouvelle classe qui prend un argument de dictionnaire lors de la création.

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

Nous pouvons également autoriser les arguments de mots clés. Voir ce post

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')
0
HarlemSquirrel

J'arrive à cela tard dans la journée, mais voici mon pennyworth avec un objet qui contient des chemins utiles dans une application, mais vous pouvez l'adapter à tout ce qui vous intéresse. (ce qui est ce que je pense que cette question est vraiment):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

C'est cool parce que maintenant:

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

Donc, ceci utilise l'objet fonction comme les réponses ci-dessus mais utilise la fonction pour obtenir les valeurs (vous pouvez toujours utiliser (getattr, x_path, 'repository') plutôt que x_path('repository') si vous préférez).

0
Paul Whipp

Quels objets utilisez-vous? J'ai juste essayé cela avec un exemple de classe et cela a bien fonctionné:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

Et j'ai 123 comme réponse.

La seule situation où je vois cet échec est si vous essayez une setattr sur un objet intégré.

Mise à jour: D'après le commentaire, il s'agit d'une répétition de: Pourquoi ne pouvez-vous pas ajouter d'attributs à un objet en python?

0
jneves