Python dictionary from an object's fields

573,314

Solution 1

Note that best practice in Python 2.7 is to use new-style classes (not needed with Python 3), i.e.

class Foo(object):
   ...

Also, there's a difference between an 'object' and a 'class'. To build a dictionary from an arbitrary object, it's sufficient to use __dict__. Usually, you'll declare your methods at class level and your attributes at instance level, so __dict__ should be fine. For example:

>>> class A(object):
...   def __init__(self):
...     self.b = 1
...     self.c = 2
...   def do_nothing(self):
...     pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}

A better approach (suggested by robert in comments) is the builtin vars function:

>>> vars(a)
{'c': 2, 'b': 1}

Alternatively, depending on what you want to do, it might be nice to inherit from dict. Then your class is already a dictionary, and if you want you can override getattr and/or setattr to call through and set the dict. For example:

class Foo(dict):
    def __init__(self):
        pass
    def __getattr__(self, attr):
        return self[attr]

    # etc...

Solution 2

Instead of x.__dict__, it's actually more pythonic to use vars(x).

Solution 3

The dir builtin will give you all the object's attributes, including special methods like __str__, __dict__ and a whole bunch of others which you probably don't want. But you can do something like:

>>> class Foo(object):
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> [name for name in dir(f) if not name.startswith('__')]
[ 'bar', 'baz' ]
>>> dict((name, getattr(f, name)) for name in dir(f) if not name.startswith('__')) 
{ 'bar': 'hello', 'baz': 'world' }

So can extend this to only return data attributes and not methods, by defining your props function like this:

import inspect

def props(obj):
    pr = {}
    for name in dir(obj):
        value = getattr(obj, name)
        if not name.startswith('__') and not inspect.ismethod(value):
            pr[name] = value
    return pr

Solution 4

I've settled with a combination of both answers:

dict((key, value) for key, value in f.__dict__.iteritems() 
    if not callable(value) and not key.startswith('__'))

Solution 5

I thought I'd take some time to show you how you can translate an object to dict via dict(obj).

class A(object):
    d = '4'
    e = '5'
    f = '6'

    def __init__(self):
        self.a = '1'
        self.b = '2'
        self.c = '3'

    def __iter__(self):
        # first start by grabbing the Class items
        iters = dict((x,y) for x,y in A.__dict__.items() if x[:2] != '__')

        # then update the class items with the instance items
        iters.update(self.__dict__)

        # now 'yield' through the items
        for x,y in iters.items():
            yield x,y

a = A()
print(dict(a)) 
# prints "{'a': '1', 'c': '3', 'b': '2', 'e': '5', 'd': '4', 'f': '6'}"

The key section of this code is the __iter__ function.

As the comments explain, the first thing we do is grab the Class items and prevent anything that starts with '__'.

Once you've created that dict, then you can use the update dict function and pass in the instance __dict__.

These will give you a complete class+instance dictionary of members. Now all that's left is to iterate over them and yield the returns.

Also, if you plan on using this a lot, you can create an @iterable class decorator.

def iterable(cls):
    def iterfn(self):
        iters = dict((x,y) for x,y in cls.__dict__.items() if x[:2] != '__')
        iters.update(self.__dict__)

        for x,y in iters.items():
            yield x,y

    cls.__iter__ = iterfn
    return cls

@iterable
class B(object):
    d = 'd'
    e = 'e'
    f = 'f'

    def __init__(self):
        self.a = 'a'
        self.b = 'b'
        self.c = 'c'

b = B()
print(dict(b))
Share:
573,314
Julio César
Author by

Julio César

Mmmmmm.

Updated on September 08, 2021

Comments

  • Julio César
    Julio César over 2 years

    Do you know if there is a built-in function to build a dictionary from an arbitrary object? I'd like to do something like this:

    >>> class Foo:
    ...     bar = 'hello'
    ...     baz = 'world'
    ...
    >>> f = Foo()
    >>> props(f)
    { 'bar' : 'hello', 'baz' : 'world' }
    

    NOTE: It should not include methods. Only fields.

  • Julio César
    Julio César over 15 years
    This code includes methods. Is there a way to exclude methods? I only need the object's fields. Thanks
  • dF.
    dF. over 15 years
    That works also, but be aware that it will only give you the attributes set on the instance, not on the class (like class Foo in your example)...
  • quamrana
    quamrana over 15 years
    So, jcarrascal, you are better off wrapping the above code in a function like props(), then you can call either props(f) or props(Foo). Notice that you are almost always better off writing a function, rather than writing 'inline' code.
  • zakdances
    zakdances about 11 years
    What happens if one of A's attribute's has a custom getter? (a function with a @property decorator)? Does it still show up in ____dict____? What will its value be?
  • Antimony
    Antimony almost 11 years
    __dict__ won't work if the object is using slots (or defined in a C module).
  • chiffa
    chiffa over 10 years
    Is there an equivalent of this method for the class objects? I.E. Instead of using f=Foo() and then doing f.__dict__, do directly Foo.__dict__?
  • Ehtesh Choudhury
    Ehtesh Choudhury over 10 years
    ismethod doesn't catch functions. Example: inspect.ismethod(str.upper). inspect.isfunction isn't much more helpful, though. Not sure how to approach this right away.
  • robert
    robert over 9 years
    Sorry, I'm coming to this late, but shouldn't vars(a) do this? For me it's preferable to invoking the __dict__ directly.
  • Tadhg McDonald-Jensen
    Tadhg McDonald-Jensen over 8 years
    for second example it would be better to do __getattr__ = dict.__getitem__ to exactly replicate the behaviour, then you would also want __setattr__ = dict.__setitem__ and __delattr__ = dict.__delitem__ for complete-ness.
  • ThorSummoner
    ThorSummoner almost 8 years
    I made some tweaks to crudely recurs and ignore all errors to a depth here, thanks! gist.github.com/thorsummoner/bf0142fd24974a0ced778768a33a306‌​9
  • tvt173
    tvt173 about 7 years
    Agreed. Note that you can also convert the other way (dict->class) by typing MyClass(**my_dict), assuming you have defined a constructor with parameters that mirror the class attributes. No need to access private attributes or override dict.
  • cowbert
    cowbert over 6 years
    The best practice in Python 2.7 is to use vars(Object) if Object inherits from object
  • maxkoryukov
    maxkoryukov over 6 years
    After this answer still not clear how to get a dictionary from an object. Not properties, but entire dictionary;)
  • maxkoryukov
    maxkoryukov over 6 years
    @robert since it is the end of 2017 (9 years after the question was asked, and this answer was accepted), so I have moved your notice about vars to the answer's body.
  • Naramsim
    Naramsim almost 6 years
    Any way to preserve the order of the keys?
  • Jacob Lee
    Jacob Lee over 5 years
    It may be worth including a warning that vars(obj) and obj.__dict__ will not provide custom class attributes (e.g. class Foo(): fooname='fooname'). Some might not expect that, since obj.class_attribute and obj.object_attribute look like similar.
  • Hugh W
    Hugh W over 5 years
    Can you explain why it's more Pythonic?
  • Berislav Lopac
    Berislav Lopac over 5 years
    First, Python generally shuns callings dunder items directly, and there is almost always a method or function (or operator) to access it indirectly. In general, dunder attributes and methods are an implementation detail, and using the "wrapper" function allows you to separate the two. Second, this way you can override the vars function and introduce additional functionality without changing the object itself.
  • Berislav Lopac
    Berislav Lopac over 5 years
    __dict__ is an attribute, not a method, so this example changes the interface (i.e. you need to call it as a callable), so it's not overriding it.
  • should_be_working
    should_be_working about 5 years
    In this case what's the solution ? Since vars() doesn't work
  • Albert
    Albert almost 5 years
    @should_be_working dir is the solution in this case. See the other answer about that.
  • c z
    c z over 4 years
    It still fails if your class uses __slots__ though.
  • Berislav Lopac
    Berislav Lopac over 4 years
    That is correct, and I always felt that it would be a good direction to extend vars to, i.e. to return an equivalent of __dict__ for "slotted" classes. For now, it can be emulated by adding a __dict__ property which returns {x: getattr(self, x) for x in self.__slots__} (not sure whether that affects the performance or behaviour in any way though).
  • mrgloom
    mrgloom over 4 years
    What is the difference between vars and __dict__?
  • Morten
    Morten over 4 years
    Nice, btw note this is for python2.7, for python3 relpace iteritems() with simply items().
  • Alex
    Alex over 4 years
    This will grab also all the methods, but we need only class+instance fields. Maybe dict((x, y) for x, y in KpiRow.__dict__.items() if x[:2] != '__' and not callable(y)) will solve it? But there still could be static methods :(
  • Alex
    Alex over 4 years
    And what about staticmethod? It's not callable.
  • Anakhand
    Anakhand over 3 years
    Agree with @cz ; and +1 to Berislav for extending vars to work with __slots__ (and even objects that don't have neither __dict__ nor __slots__). For now, I have written a wrapper function to deal with this in an answer below; I'd say it's better to add this functionality externally rather than require your object to define a __dict__ property (which might not be possible).
  • Zack Plauché
    Zack Plauché almost 3 years
    vars does not return class attributes, only instance attributes (set by __init__)