140

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

smci
  • 32,567
  • 20
  • 113
  • 146
OscarRyz
  • 196,001
  • 113
  • 385
  • 569

8 Answers8

216

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)
Ian Clelland
  • 43,011
  • 8
  • 86
  • 87
  • 4
    Offering both the `Employee(some_dict)` and the `Employee(**some_dict)` APIs is inconsistent. Whichever is better should be supplied. – Mike Graham Mar 17 '10 at 22:46
  • 1
    (Also, you mean `if initial_data is not None`; in a strange enough circumstance, this could introduce code that does not work as intended. Also, you can't use a the key `'initial_data'` now using one of the APIs.) – Mike Graham Mar 17 '10 at 22:47
  • 4
    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 Mar 17 '10 at 22:55
  • @Matt Anderson, that code seems a bit clever to me. It seems like a more readable solution would be logic to the effect that Ian used or, better yet, to choose a single, consistent API. – Mike Graham Mar 17 '10 at 23:01
  • @Mike Graham: Agreed, on both counts; although offering both interfaces is exactly what the python dict constructor does. I've updated the example to take any number of mapping objects, without reserving any names (except, perhaps necessarily, 'self') – Ian Clelland Mar 17 '10 at 23:02
  • @Ian Clelland, Much to its discredit if you ask me! ;) Good point, though. – Mike Graham Mar 17 '10 at 23:04
  • for key, value in initial_data.items(): looks more pythonic – Luka Rahne Mar 18 '10 at 00:08
  • 5
    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. Oct 03 '13 at 20:56
50

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.

Mike Graham
  • 73,987
  • 14
  • 101
  • 130
  • And what if the scenario lies somewhere between your two extremes? That's precisely the use case for this, and currently AFAICT there is no way to do this in a DRY and pythonic way. – DylanYoung Jun 23 '20 at 20:23
  • It's worth noting that the well known Pandas library uses attributes for dynamic access. – Erik Sep 16 '21 at 09:34
21

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
Dave Kirby
  • 25,806
  • 5
  • 67
  • 84
  • 5
    `__dict__` is an implementation artifact and should not be used. Also, this ignores the existence of descriptors on the class. – Ignacio Vazquez-Abrams Mar 17 '10 at 22:03
  • 1
    @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 Mar 17 '10 at 22:08
  • 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 Mar 17 '10 at 22:14
  • 17
    `__dict__` is a documented part of the language, not an implementation artifact. – Dave Kirby Mar 17 '10 at 22:14
  • @Dave is this it? http://docs.python.org/library/stdtypes.html#special-attributes – OscarRyz Mar 17 '10 at 22:25
  • If you know the class does not have any descriptors then `__dict__.update` is perfectly valid - it is done in lots of places in the standard library. – Dave Kirby Mar 17 '10 at 22:25
  • 3
    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 Mar 17 '10 at 22:34
  • 4
    @DaveKirby: It seems the general use of ``__dict__`` is advised against: https://docs.python.org/tutorial/classes.html#id2 – equaeghe Apr 13 '14 at 22:43
18

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)

RufusVS
  • 4,008
  • 3
  • 29
  • 40
13

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)
zdarkwhite
  • 346
  • 4
  • 4
9

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.

van
  • 74,297
  • 13
  • 168
  • 171
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'
Alex Spencer
  • 834
  • 2
  • 12
  • 23
  • This doesn't turn those dictionary key/value pairs into attribute/value pairs. It still requires string indexing, hence it's just a 'dictionary with extra steps'. – Alex Povel May 24 '22 at 13:18
0

If you don't mind using a library:

pip install domonic

then you can do:

from domonic.javascript import Object

class Employee(Object):
    pass

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

e = Employee(d)
print(e.name)
print(e['name'])
print(e)

# {'name': 'Oscar', 'lastName': 'Reyes', 'age': 32}

Which should behave as required.

byteface
  • 152
  • 2
  • 7