Imposta gli attributi dal dizionario in Python


Risposte:


172

Certo, qualcosa del genere:

class Employee(object):
    def __init__(self, initial_data):
        for key in initial_data:
            setattr(self, key, initial_data[key])

Aggiornare

Come suggerisce Brent Nash, puoi renderlo più flessibile consentendo anche argomenti di parole chiave:

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

Quindi puoi chiamarlo in questo modo:

e = Employee({"name": "abc", "age": 32})

o così:

e = Employee(name="abc", age=32)

o anche così:

employee_template = {"role": "minion"}
e = Employee(employee_template, name="abc", age=32)

4
Se passi i dati iniziali mentre def __init__(self,**initial_data)ottieni il vantaggio aggiuntivo di avere un metodo init che può anche fare argomenti con parole chiave (ad es. "E = Employee (name = 'Oscar')" o semplicemente inserire un dizionario (ad es. "E = Employee ( ** dict) ").
Brent Writes Code

2
L'offerta di API Employee(some_dict)e Employee(**some_dict)API non è coerente. Qualunque sia il migliore dovrebbe essere fornito.
Mike Graham

4
Se si imposta predefinito del arg al ()posto di None, si potrebbe fare in questo modo: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Matt Anderson

4
So che è una vecchia domanda, ma voglio solo aggiungere che può essere fatta in due righe con la comprensione della lista, ad esempio:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ

2
Mi piacerebbe sapere perché questo non è integrato in Python in qualche modo semplice. Lo sto usando per gestire il lancio dell'API GeoIP2 di python AddressNotFoundError(per restituire i dati corretti in quel caso, piuttosto che esplodere) - Ho trovato pazzesco che qualcosa di così semplice in PHP ( (object) ['x' => 'y']) richiedesse così tanto cruft in Python
Someguy123

46

L'impostazione degli attributi in questo modo non è quasi certamente il modo migliore per risolvere un problema. O:

  1. Sai cosa dovrebbero essere tutti i campi in anticipo. In tal caso, puoi impostare esplicitamente tutti gli attributi. Questo sembrerebbe

    class Employee(object):
        def __init__(self, name, last_name, age):
            self.name = name
            self.last_name = last_name
            self.age = age
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
    e = Employee(**d) 
    
    print e.name # Oscar 
    print e.age + 10 # 42 

    o

  2. Non sai cosa dovrebbero essere tutti i campi in anticipo. In questo caso, dovresti memorizzare i dati come un dict invece di inquinare uno spazio dei nomi degli oggetti. Gli attributi sono per l'accesso statico. Questo caso sarebbe simile

    class Employee(object):
        def __init__(self, data):
            self.data = data
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
    e = Employee(d) 
    
    print e.data['name'] # Oscar 
    print e.data['age'] + 10 # 42 

Un'altra soluzione che è sostanzialmente equivalente al caso 1 consiste nell'usare a collections.namedtuple. Vedi la risposta di van per come implementarlo.


E se lo scenario si trova da qualche parte tra i tuoi due estremi? Questo è esattamente il caso d'uso per questo, e attualmente AFAICT non c'è modo di farlo in modo DRY e pitonico.
DylanYoung

15

È possibile accedere agli attributi di un oggetto con __dict__e chiamare il metodo di aggiornamento su di esso:

>>> class Employee(object):
...     def __init__(self, _dict):
...         self.__dict__.update(_dict)
... 


>>> dict = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }

>>> e = Employee(dict)

>>> e.name
'Oscar'

>>> e.age
32

3
__dict__è un artefatto di implementazione e non deve essere utilizzato. Inoltre, questo ignora l'esistenza di descrittori nella classe.
Ignacio Vazquez-Abrams

1
@Ignacio cosa intendi con "artefatto di implementazione"? Di cosa non dovremmo esserne consapevoli? O che potrebbe non essere presente su piattaforme diverse? (es. Python in Windows vs Python in Linux) Quale sarebbe una risposta accettabile?
OscarRyz

12
__dict__è una parte documentata del linguaggio, non un artefatto di implementazione.
Dave Kirby

3
L'uso setattrè preferibile all'accesso __dict__diretto. Devi tenere a mente molte cose che potrebbero portare a __dict__non essere lì o non fare quello che vuoi quando lo usi __dict__, ma setattrè praticamente identico a farlo effettivamente foo.bar = baz.
Mike Graham

1
@DaveKirby: Sembra che l'uso generale di __dict__sia sconsigliato: docs.python.org/tutorial/classes.html#id2
equaeghe

11

Perché non usare semplicemente i nomi degli attributi come chiavi di un dizionario?

class StructMyDict(dict):

     def __getattr__(self, name):
         try:
             return self[name]
         except KeyError as e:
             raise AttributeError(e)

     def __setattr__(self, name, value):
         self[name] = value

Puoi inizializzare con argomenti con nome, un elenco di tuple o un dizionario o assegnazioni di attributi individuali, ad esempio:

nautical = StructMyDict(left = "Port", right = "Starboard") # named args

nautical2 = StructMyDict({"left":"Port","right":"Starboard"}) # dictionary

nautical3 = StructMyDict([("left","Port"),("right","Starboard")]) # tuples list

nautical4 = StructMyDict()  # fields TBD
nautical4.left = "Port"
nautical4.right = "Starboard"

for x in [nautical, nautical2, nautical3, nautical4]:
    print "%s <--> %s" % (x.left,x.right)

In alternativa, invece di generare l'errore di attributo, puoi restituire Nessuno per valori sconosciuti. (Un trucco usato nella classe di archiviazione web2py)


7

Penso che l'utilizzo delle risposte settattrsia la strada da percorrere se hai davvero bisogno di supporto dict.

Ma se Employeeobject è solo una struttura a cui puoi accedere con dot syntax ( .name) invece di dict syntax ( ['name']), puoi usare namedtuple in questo modo:

from collections import namedtuple

Employee = namedtuple('Employee', 'name age')
e = Employee('noname01', 6)
print e
#>> Employee(name='noname01', age=6)

# create Employee from dictionary
d = {'name': 'noname02', 'age': 7}
e = Employee(**d)
print e
#>> Employee(name='noname02', age=7)
print e._asdict()
#>> {'age': 7, 'name': 'noname02'}

Hai un _asdict()metodo per accedere a tutte le proprietà come dizionario, ma non puoi aggiungere ulteriori attributi in seguito, solo durante la costruzione.


6

diciamo per esempio

class A():
    def __init__(self):
        self.x=7
        self.y=8
        self.z="name"

se vuoi impostare gli attributi in una volta

d = {'x':100,'y':300,'z':"blah"}
a = A()
a.__dict__.update(d)

1
puoi usare chiave / valori per comodità:a.__dict__.update(x=100, y=300, z="blah")
simno

1

simile all'utilizzo di un dict, potresti semplicemente usare kwargs in questo modo:

class Person:
   def __init__(self, **kwargs):
       self.properties = kwargs

   def get_property(self, key):
       return self.properties.get(key, None)

   def main():
       timmy = Person(color = 'red')
       print(timmy.get_property('color')) #prints 'red'
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.