Python 101: How to Change a Dict Into a Class
I work with a lot of dictionaries at my job. Sometimes the dictionaries get really complicated with lots of nested data structures embedded within them. Recently I got a little tired of trying to remember all the keys in my dictionaries so I decided to change one of my dictionaries into a class so I could access the keys as instance variables / attributes. If you’ve ever gotten sick
Here’s one simple way to do it:
######################################################################## class Dict2Obj(object): """ Turns a dictionary into a class """ #---------------------------------------------------------------------- def __init__(self, dictionary): """Constructor""" for key in dictionary: setattr(self, key, dictionaryCreate object from dict python) #---------------------------------------------------------------------- if __name__ == "__main__": ball_dict = ball = Dict2Obj(ball_dict)
This code uses setattr to add each of the keys as attributes to the class. The following shows some examples of how it works:
>>> ball.color ‘blue’ >>> ball.size ‘8 inches’ >>> print ball
When we print the ball object, we get a rather unhelpful string back from the class. Let’s override the __repr__ method of our class and make it print out something a little more useful:
######################################################################## class Dict2Obj(object): """ Turns a dictionary into a class """ #---------------------------------------------------------------------- def __init__(self, dictionary): """Constructor""" for key in dictionary: setattr(self, key, dictionaryCreate object from dict python) #---------------------------------------------------------------------- def __repr__(self): """""" return "" % self.__dict__ #---------------------------------------------------------------------- if __name__ == "__main__": ball_dict = ball = Dict2Obj(ball_dict)
Now if we print out the ball object, we’ll get the following:
This is a little unintuitive in that it is printing out a dictionary using the class’s internal __dict__ rather than just the attribute names. This is more a matter of taste than anything, but let’s try to get just the method names:
######################################################################## class Dict2Obj(object): """ Turns a dictionary into a class """ #---------------------------------------------------------------------- def __init__(self, dictionary): """Constructor""" for key in dictionary: setattr(self, key, dictionaryCreate object from dict python) #---------------------------------------------------------------------- def __repr__(self): """""" attrs = str([x for x in self.__dict__]) return "" % attrs #---------------------------------------------------------------------- if __name__ == "__main__": ball_dict = ball = Dict2Obj(ball_dict)
Here we just loop over the contents of __dict__ and return a string with just a list of the keys, which match up with the attribute names. You could have also done it like this:
attrs = str([x for x in dir(self) if "__" not in x])
I’m sure there are lots of other ways to accomplish this sort of thing as well. Regardless, I found this little piece of code helpful in some of my work. Hopefully you’ll find it useful too.
How to convert a nested Python dict to object?
I’m searching for an elegant way to get data using attribute access on a dict with some nested dicts and lists (i.e. javascript-style object syntax). For example:
>>> x = dict2obj(d) >>> x.a 1 >>> x.b.c 2 >>> x.d[1].foo bar
I think, this is not possible without recursion, but what would be a nice way to get an object style for dicts?
I was trying to do something similar recently, but a recurring dictionary key («from» — which is a Python keyword) prevented me from going through with it. Because as soon as you tried using «x.from» to access that attribute you’d get a syntax error.
that’s a problem indeed, but i can abandon on «from» to make life easier in accessing large dict constructs 🙂 typing x[‘a’][‘d’][1][‘foo’] is really annoying, so x.a.d[1].foo rules. if you need from, you can access it via getattr(x, ‘from’) or use _from as attribute instead.
Most of these «solutions» don’t seem to work (even the accepted one, doesn’t allow nested d1.b.c ), I think it’s clear you should be using something from a library, e.g. namedtuple from collections, as this answer suggests, .
45 Answers 45
class Dict2Obj: def __init__(self, json_data): self.convert(json_data) def convert(self, json_data): if not isinstance(json_data, dict): return for key in json_data: if not isinstance(json_dataCreate object from dict python, dict): self.__dict__.update() else: self.__dict__.update(< key: Dict2Obj(json_dataCreate object from dict python)>)
I could not find the implementation of nested dictionary to object, so wrote one.
>>> json_data = , "c": 3> >>> out_obj = Dict2Obj(json_data) >>> out_obj.a >>> out_obj.a.b 2 >>> out_obj.a.c 3
My dictionary is of this format:
As can be seen, I have nested dictionaries and list of dicts. This is because the addr_bk was decoded from protocol buffer data that converted to a python dict using lwpb.codec. There are optional field (e.g. email => where key may be unavailable) and repeated field (e.g. phone => converted to list of dict).
I tried all the above proposed solutions. Some doesn’t handle the nested dictionaries well. Others cannot print the object details easily.
Only the solution, dict2obj(dict) by Dawie Strauss, works best.
I have enhanced it a little to handle when the key cannot be found:
# Work the best, with nested dictionaries & lists! :) # Able to print out all items. class dict2obj_new(dict): def __init__(self, dict_): super(dict2obj_new, self).__init__(dict_) for key in self: item = selfCreate object from dict python if isinstance(item, list): for idx, it in enumerate(item): if isinstance(it, dict): item[idx] = dict2obj_new(it) elif isinstance(item, dict): selfCreate object from dict python = dict2obj_new(item) def __getattr__(self, key): # Enhanced to handle key not found. if self.has_key(key): return selfCreate object from dict python else: return None
# Testing. ab = dict2obj_new(addr_bk) for person in ab.person: print "Person ID:", person.id print " Name:", person.name # Check if optional field is available before printing. if person.email: print " E-mail address:", person.email # Check if optional field is available before printing. if person.phone: for phone_number in person.phone: if phone_number.type == codec.enums.PhoneType.MOBILE: print " Mobile phone #:", elif phone_number.type == codec.enums.PhoneType.HOME: print " Home phone #:", else: print " Work phone #:", print phone_number.number
Set attributes from dictionary in python
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
8 Answers 8
class Employee(object): def __init__(self, initial_data): for key in initial_data: setattr(self, key, initial_dataCreate object from dict python)
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, dictionaryCreate object from dict python) for key in kwargs: setattr(self, key, kwargsCreate object from dict python)
Then you can call it like this:
employee_template = e = Employee(employee_template, name="abc", age=32)
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,dCreate object from dict python) for key in d] for d in some_dict]
Setting attributes in this way is almost certainly not the best way to solve a problem. Either:
- 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 = e = Employee(**d) print e.name # Oscar print e.age + 10 # 42
class Employee(object): def __init__(self, data): self.data = data d = 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.
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.
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 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?
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
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() # 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)