Legen Sie Attribute aus dem Wörterbuch in Python fest


102

Ist es möglich, ein Objekt aus einem Wörterbuch in Python so zu erstellen, dass jeder Schlüssel ein Attribut dieses Objekts ist?

Etwas wie das:

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

 e = Employee(d) 
 print e.name # Oscar 
 print e.age + 10 # 42 

Ich denke, es wäre so ziemlich das Gegenteil dieser Frage: Python-Wörterbuch aus den Feldern eines Objekts

Antworten:


171

Klar, so etwas:

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

Aktualisieren

Wie Brent Nash vorschlägt, können Sie dies flexibler gestalten, indem Sie auch Keyword-Argumente zulassen:

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

Dann können Sie es so nennen:

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

oder so:

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

oder sogar so:

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

4
Wenn Sie anfängliche Daten übergeben, erhalten def __init__(self,**initial_data)Sie den zusätzlichen Vorteil einer Init-Methode, die auch Schlüsselwortargumente ausführen kann (z. B. "e = Mitarbeiter (Name = 'Oscar')" oder einfach ein Wörterbuch aufnehmen (z. B. "e = Mitarbeiter (") ** diktieren) ").
Brent schreibt Code

2
Das Anbieten Employee(some_dict)der Employee(**some_dict)APIs und der APIs ist inkonsistent. Was besser ist, sollte geliefert werden.
Mike Graham

4
Wenn Sie die Standardeinstellung Ihres Args auf ()anstatt setzen None, können Sie dies folgendermaßen tun : def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Matt Anderson

4
Ich weiß, dass es eine alte Frage ist, aber ich möchte nur hinzufügen, dass es in zwei Zeilen mit Listenverständnis gemacht werden kann, zum Beispiel:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ

2
Ich würde gerne wissen, warum dies nicht auf einfache Weise in Python eingebaut ist. Ich benutze dies, um mit den Python-GeoIP2-API-Auslösungen umzugehen AddressNotFoundError(um in diesem Fall korrigierte Daten zurückzugeben, anstatt sie in (object) ['x' => 'y']die Luft zu
jagen

46

Das Festlegen von Attributen auf diese Weise ist mit ziemlicher Sicherheit nicht der beste Weg, um ein Problem zu lösen. Entweder:

  1. Sie wissen, was alle Felder im Voraus sein sollten. In diesem Fall können Sie alle Attribute explizit festlegen. Das würde so aussehen

    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 

    oder

  2. Sie wissen nicht, was alle Felder im Voraus sein sollten. In diesem Fall sollten Sie die Daten als Diktat speichern, anstatt einen Objektnamespace zu verschmutzen. Attribute sind für den statischen Zugriff. Dieser Fall würde so aussehen

    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 

Eine andere Lösung, die im Wesentlichen Fall 1 entspricht, ist die Verwendung von a collections.namedtuple. Siehe Vans Antwort, wie man das umsetzt.


Und was ist, wenn das Szenario irgendwo zwischen Ihren beiden Extremen liegt? Das ist genau der Anwendungsfall dafür, und derzeit gibt es AFAICT keine Möglichkeit, dies auf trockene und pythonische Weise zu tun.
DylanYoung

14

Sie können mit auf die Attribute eines Objekts zugreifen __dict__und die Aktualisierungsmethode dafür aufrufen:

>>> 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__ist ein Implementierungsartefakt und sollte nicht verwendet werden. Dies ignoriert auch die Existenz von Deskriptoren in der Klasse.
Ignacio Vazquez-Abrams

1
@Ignacio was meinst du mit "Implementierungsartefakt"? Was sollten wir uns nicht bewusst sein? Oder dass es möglicherweise nicht auf verschiedenen Plattformen vorhanden ist? (zB Python unter Windows vs. Python unter Linux) Was wäre eine akzeptable Antwort?
OscarRyz

12
__dict__ist ein dokumentierter Teil der Sprache, kein Implementierungsartefakt.
Dave Kirby

3
Die Verwendung setattrist dem __dict__direkten Zugriff vorzuziehen . Sie müssen viele Dinge im Auge behalten, die dazu führen können, dass Sie __dict__nicht da sind oder nicht das tun, was Sie wollen, wenn Sie es verwenden __dict__, aber es setattrist praktisch identisch mit dem tatsächlichen Tun foo.bar = baz.
Mike Graham

1
@ DaveKirby: Es scheint, dass die allgemeine Verwendung von davon __dict__abgeraten wird: docs.python.org/tutorial/classes.html#id2
Equaeghe

10

Warum nicht einfach Attributnamen als Schlüssel für ein Wörterbuch verwenden?

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

Sie können mit benannten Argumenten, einer Liste von Tupeln oder einem Wörterbuch oder einzelnen Attributzuweisungen initialisieren, z.

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)

Anstatt den Attributfehler auszulösen, können Sie alternativ None für unbekannte Werte zurückgeben. (Ein Trick, der in der web2py-Speicherklasse verwendet wird)


7

Ich denke, dass die Verwendung von Antworten der richtige settattrWeg ist, wenn Sie wirklich Unterstützung benötigen dict.

Wenn das EmployeeObjekt jedoch nur eine Struktur ist, auf die Sie mit dot syntax ( .name) anstelle von dict syntax ( ['name']) zugreifen können, können Sie namedtuple wie folgt verwenden :

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

Sie haben zwar eine _asdict()Methode, um auf alle Eigenschaften als Wörterbuch zuzugreifen, können jedoch später, nur während der Erstellung, keine zusätzlichen Attribute hinzufügen.


6

sagen zum Beispiel

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

wenn Sie die Attribute sofort festlegen möchten

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

1
Sie können Schlüssel / Werte zur Vereinfachung verwenden:a.__dict__.update(x=100, y=300, z="blah")
Simno

1

Ähnlich wie bei der Verwendung eines Diktats können Sie auch kwargs wie folgt verwenden:

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'
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.