74

Is it possible to create an object from a dictionary in python in such a way that each key is an attribute of that object?

Something like this:

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

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

I think it would be pretty much the inverse of this question: Python dictionary from an object's fields


  • side remark: dict is not a great name for variable, it hides the type dict - van

7 답변


124

Sure, something like this:

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

Update

As Brent Nash suggests, you can make this more flexible by allowing keyword arguments as well:

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

Then you can call it like this:

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

or like this:

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

or even like this:

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


  • If you pass initial data as def __init__(self,**initial_data) you get the added benefit of having an init method that can also do keyword arguments too (e.g. "e = Employee(name='Oscar')" or just take in a dictionary (e.g. "e = Employee(**dict)"). - Brent Writes Code
  • Offering both the Employee(some_dict) and the Employee(**some_dict) APIs is inconsistent. Whichever is better should be supplied. - Mike Graham
  • If you set your arg's default to () instead of None, you could do it like so: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs). - Matt Anderson
  • I know it is old question, but I just want to add that it can be done in two lines with list comprehension, for example: [[setattr(self,key,d[key]) for key in d] for d in some_dict] - T.Z.
  • I would love to know why this isn't built into python in some simple way. I'm using this to deal with the python GeoIP2 API throwing AddressNotFoundError's (to return corrected data in that case, rather than blowing up) - I found it crazy that something so easy in PHP ((object) ['x' => 'y']) required so much cruft in Python - Someguy123

35

Setting attributes in this way is almost certainly not the best way to solve a problem. Either:

  1. You know what all the fields should be ahead of time. In that case, you can set all the attributes explicitly. This would look like

    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 
    

    or

  2. You don't know what all the fields should be ahead of time. In this case, you should store the data as a dict instead of polluting an objects namespace. Attributes are for static access. This case would look like

    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 
    

Another solution that is basically equivalent to case 1 is to use a collections.namedtuple. See van's answer for how to implement that.


12

You can access the attributes of an object with __dict__, and call the update method on it:

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


  • __dict__ is an implementation artifact and should not be used. Also, this ignores the existence of descriptors on the class. - Ignacio Vazquez-Abrams
  • @Ignacio what do you mean with "implementation artifact"? What we shouldn't not be aware of it? Or that it may not be present in different platforms? ( eg. Python in Windows vs. Python on Linux ) What would be an acceptable answer? - OscarRyz
  • I was going to say that there's no guarantee of it existing in a given Python implementation, but it is referred to multiple times in the langref. Ian's answer covers the other concern, i.e. passing it into a descriptor instead of clobbering it. - Ignacio Vazquez-Abrams
  • __dict__ is a documented part of the language, not an implementation artifact. - Dave Kirby
  • Using setattr is preferable to accessing __dict__ directly. You have to keep in mind a lot of things that could lead to __dict__ not being there or not doing what you want it to when you use __dict__, but setattr is virtually identical to actually doing foo.bar = baz. - Mike Graham

6

I think that answer using settattr are the way to go if you really need to support dict.

But if Employee object is just a structure which you can access with dot syntax (.name) instead of dict syntax (['name']), you can use namedtuple like this:

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

You do have _asdict() method to access all properties as dictionary, but you cannot add additional attributes later, only during the construction.


  • It's best to refer to attributes as "attributes" rather than "properties", since the latter term can be confused with what you get when you use property. - Mike Graham
  • good point - fixed - van

6

Why not just use attribute names as keys to a dictionary?

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

You can initialize with named arguments, a list of tuples, or a dictionary, or individual attribute assignments, e.g.:

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)

Alternatively, instead of raising the attribute error, you can return None for unknown values. (A trick used in the web2py storage class)


5

say for example

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

if you want to set the attributes at once

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


  • you may use key/values for convenience: a.__dict__.update(x=100, y=300, z="blah") - simno

1

similar to using a dict, you could just use kwargs like so:

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'

Linked


Related

Latest