Come posso creare un oggetto e aggiungere attributi ad esso?


311

Voglio creare un oggetto dinamico (all'interno di un altro oggetto) in Python e quindi aggiungere attributi ad esso.

Provai:

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

ma questo non ha funzionato.

Qualche idea?

modificare:

Sto impostando gli attributi da un forciclo che scorre attraverso un elenco di valori, ad es

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

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

Nell'esempio di cui sopra che avrei avuto obj.a.attr1, obj.a.attr2, obj.a.attr3.

Ho usato la setattrfunzione perché non sapevo come fare obj.a.NAMEda un forciclo.

Come impostare l'attributo in base al valore pdell'esempio sopra?


4
Cosa intendi con "non ha funzionato"? Presumo che abbia sollevato un'eccezione AttributeError, giusto?
Josh Wright,

1
si. L'oggetto 'object' non ha alcun attributo 'somefield'
John,

6
Perché stai facendo questo? Un "oggetto" generico non ha alcun significato reale . Qual è il significato della cosa che stai creando? Perché non è una classe corretta o namedtuple?
S.Lott

1
L'esempio non è minimo e confuso per me o semplicemente non vedo perché non lavori con alcuni a = object()e ne hai bisogno obj.a = object(). Ancora una volta sto parlando dell'esempio, nel tuo codice reale un oggetto all'interno di un oggetto potrebbe essere utile.
kon psych il

Risposte:


215

Potresti usare la mia antica ricetta del mazzo , ma se non vuoi creare una "classe di mazzo", in Python esiste già una classe molto semplice: tutte le funzioni possono avere attributi arbitrari (incluse le funzioni lambda). Quindi, i seguenti lavori:

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

Se la perdita di chiarezza rispetto alla venerabile Bunchricetta è OK, è una decisione di stile che ovviamente lascerò a te.


25
@FogleBird, una decisione di stile, come ho già detto. Alcuni esperti di CS formati ad esempio nel calcolo lambda di Church sono abituati a pensare alle funzioni (lambda) come il tipo fondamentale di tutti i dati (l'intero 23, ad esempio, può essere visto come equivalente a lambda: 23), quindi a tali esperti che usano lambdas a questo scopo presumibilmente non proverei nulla di simile a "un hack". Personalmente, non mi piace molto lambda in Python , ma è un problema di gusti personali.
Alex Martelli,

E in alcuni casi, considerare se il lambdamodello si adatta o meno al tuo caso d'uso può portarti a rendersi conto che qualcosa che inizialmente avevi pensato come dati è in realtà più simile a una funzione - o, in ogni caso, a un funzione.
Kyle Strand,

5
@ naught101, una funzione è un oggetto, in Python, quindi la tua obiezione è insondabile.
Alex Martelli,

6
@ naught101, evitare la creazione di un nuovo tipo (riutilizzandone uno esistente) non complica, semplifica. Al giorno d'oggi, in realtà, preferirei from argparse import Namespaceanche se vorrei che vivesse altrove * ad es. collection) - riutilizzando di nuovo un tipo ora esistente, solo uno migliore, evitando ancora la creazione di nuovi tipi. Ma allora non c'era :-).
Alex Martelli,

1
Vedi la risposta sotto di "JF Sebastian" riguardo a SimpleNamespace dal modulo types. Se la tua versione di python lo supporta, questa è la soluzione migliore (ed è esattamente ciò per cui è progettato SimpleNamespace)
Tim Richardson,

334

Il built-in objectpuò essere istanziato ma non può avere alcun attributo impostato su di esso. (Vorrei che potesse, per questo scopo preciso.) Non ha un __dict__a contenere gli attributi.

In genere faccio solo questo:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Quando posso, conferisco alla Objectclasse un nome più significativo, a seconda del tipo di dati che sto inserendo.

Alcune persone fanno una cosa diversa, in cui usano una sottoclasse dictche consente l'accesso agli attributi alle chiavi. ( d.keyinvece di d['key'])

Modifica : per aggiungere una domanda, usare setattrva bene. Non ci si può utilizzare setattrsu object()istanze.

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

9
si può essere istanziato, semplicemente non utilizzato per qualcosa di utile una volta che è stato fatto. foo = object()funziona, ma non puoi farci molto
Daniel DiPaolo,

Ciao. Grazie per la risposta. Ho aggiornato il mio problema sopra. vedi la modifica. conosci la risposta a questo?
Giovanni,

scusa, voglio ancora impostarlo sull'oggetto. vedi aggiornamento sopra.
Giovanni,

Mi piace molto la tua risposta e penso che in futuro mi spingerò in questa direzione. Ho usato tutto il resto in questo post tranne questa metodologia molto semplice, comprensibile e leggibile. Usare type....o lambda non è mai stato il mio preferito, come il testo vomito nel mio codice. Ma questa idea è ottima per usare oggetti per contenere proprietà. Lascia il codice più leggibile in b / c quando vedo lambda rallento la mia lettura al 25% mentre la tua strada ha un senso totale! Grazie.
Marc,

ottima risposta, l'unica cosa che ho cambiato è stato usare Structcome nome della classe per renderlo più ovvio. Mi ha salvato un sacco di battitura ["e "], Saluti!
pragman,

137

C'è types.SimpleNamespaceclasse in Python 3.3+ :

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

collections.namedtuple, typing.NamedTuplepotrebbe essere utilizzato per oggetti immutabili. PEP 557 - Le classi di dati suggeriscono un'alternativa mutabile.

Per una funzionalità più ricca, è possibile provare il attrspacchetto . Vedi un esempio di utilizzo .


3
Se hai bisogno di qualcosa che argparse.Namespace
funzioni

D'accordo - Sarei curioso di sapere se c'è un aspetto negativo qui, ma questo è un prezzo incredibilmente utile per Python 3.3+.
ghukill,

Dannazione! questo non è disponibile su 2.7?
Roel,

Il attrspacchetto @Roel supporta Python 2.7
jfs

Questa mi sembra una soluzione migliore di unittest.mock; quest'ultimo è un po 'troppo pesante e un po' più malleabile. Con un oggetto simulato, la semplice assegnazione a un attributo lo farà nascere; SimpleNamespace resisterà a questo.
jdzions

32

Ci sono alcuni modi per raggiungere questo obiettivo. Fondamentalmente hai bisogno di un oggetto che sia estensibile.

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

obj.b = lambda:None

class Test:
  pass
obj.c = Test()

14
obj.a = type('', (), {})
iman


21

Ora puoi farlo (non sono sicuro che si tratti della stessa risposta di evilpie):

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

La risposta di @ evilpie imposta gli attributi direttamente su MyObject (la classe), non nella sua istanza come la tua.
jfs,

18

Prova il codice qui sotto:

$ 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']

1
Puoi spiegare di più di ciò che fa? mentre il codice può essere utile per risolvere questo problema, spiegarlo può andare molto più in là di un solo problema.
DeadChex,

1
@DeadChex Chiaramente crea una nuova Classe (oggetto) che è una classe vuota con proprietà dell'oggetto e memorizza gli attributi all'interno della classe. È ancora meglio che installare più moduli o fare affidamento su lambdas.
m3nda,

2
Non sono sicuro del perché questo non abbia più voti. C'è un motivo per non usarlo per una classe contenitore di base? Sembra funzionare bene in Python 2.7, 2.6 e 3.4
user5359531

17

Puoi anche usare direttamente un oggetto classe; crea uno spazio dei nomi:

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

9

come dicono i documenti :

Nota : objectnon non hanno una __dict__, quindi non è possibile assegnare attributi arbitrari ad un'istanza della objectclasse.

Potresti semplicemente usare un'istanza di classe fittizia.


2

Queste soluzioni sono molto utili durante i test. Sulla base delle risposte di tutti, lo faccio in Python 2.7.9 (senza metodo statico ottengo un TypeError (metodo non associato ...):

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

1

Quali oggetti stai usando? L'ho appena provato con una classe di esempio e ha funzionato bene:

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

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

E ho ottenuto 123 la risposta.

L'unica situazione in cui vedo questo errore è se stai provando un setattroggetto incorporato.

Aggiornamento: dal commento questa è una ripetizione di: Perché non puoi aggiungere attributi all'oggetto in Python?


bc è impostato su object () non una classe definita
John,

0

In arrivo a fine giornata, ma ecco il mio pennyworth con un oggetto che sembra contenere alcuni percorsi utili in un'app ma puoi adattarlo a tutto ciò che desideri una sorta di dettato di informazioni a cui puoi accedere con getattr e dot notation (che è quello su cui penso questa domanda riguardi davvero):

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))

Questo è bello perché ora:

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

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

Quindi questo usa l'oggetto funzione come le risposte sopra ma usa la funzione per ottenere i valori (puoi comunque usare (getattr, x_path, 'repository')piuttosto che x_path('repository')se preferisci).


0

Se siamo in grado di determinare e aggregare tutti gli attributi e i valori insieme prima di creare l'oggetto nidificato, potremmo creare una nuova classe che accetta un argomento del dizionario durante la creazione.

# 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'

Possiamo anche consentire argomenti per parole chiave. Vedi questo 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')

-2
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")

-2

Altrimenti vedo, in questo modo:

import maya.cmds

def getData(objets=None, attrs=None):
    di = {}
    for obj in objets:
        name = str(obj)
        di[name]=[]
        for at in attrs:
            di[name].append(cmds.getAttr(name+'.'+at)[0])
    return di

acns=cmds.ls('L_vest_*_',type='aimConstraint')
attrs=['offset','aimVector','upVector','worldUpVector']

getData(acns,attrs)

puoi aggiungere in append il nome attr this di [name] .append ([at, cmds.getAttr (name + '.' + at) [0]])
Pablo Emmanuel De Leo

1
Questo sta aggiungendo una dipendenza non standard molto grande mentre un semplice class a: passfornisce tutta la potenza richiesta.
Alexis Paques,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.