142

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

0

8 Answers 8

217

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)
Sign up to request clarification or add additional context in comments.

8 Comments

Offering both the Employee(some_dict) and the Employee(**some_dict) APIs is inconsistent. Whichever is better should be supplied.
(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.)
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, 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.
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]
|
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.

2 Comments

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.
It's worth noting that the well known Pandas library uses attributes for dynamic access.
22

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

8 Comments

__dict__ is an implementation artifact and should not be used. Also, this ignores the existence of descriptors on the class.
@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?
__dict__ is a documented part of the language, not an implementation artifact.
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.
@DaveKirby: It seems the general use of __dict__ is advised against: docs.python.org/tutorial/classes.html#id2
|
20

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)

Comments

14

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)

1 Comment

you may use key/values for convenience: a.__dict__.update(x=100, y=300, z="blah")
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.

Comments

0

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'

1 Comment

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'.
-2

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.

1 Comment

what are the advantages of this external libraries?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.