491

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.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
Julio César
  • 12,790
  • 10
  • 38
  • 45

20 Answers20

596

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...
maxkoryukov
  • 4,205
  • 5
  • 33
  • 54
user6868
  • 8,550
  • 2
  • 17
  • 3
  • 4
    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? – zakdances Mar 01 '13 at 09:36
  • 15
    `__dict__` won't work if the object is using slots (or defined in a C module). – Antimony May 27 '13 at 05:13
  • 1
    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__? – chiffa Sep 25 '13 at 11:31
  • 75
    Sorry, I'm coming to this late, but shouldn't `vars(a)` do this? For me it's preferable to invoking the `__dict__` directly. – robert Feb 12 '15 at 15:19
  • 3
    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. – Tadhg McDonald-Jensen Feb 12 '16 at 18:46
  • The best practice in Python 2.7 is to use `vars(Object)` if `Object` inherits from `object` – cowbert Nov 01 '17 at 23:39
  • 1
    @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. – maxkoryukov Nov 10 '17 at 12:21
  • Any way to preserve the order of the keys? – Naramsim May 30 '18 at 14:02
  • 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. – Jacob Lee Dec 05 '18 at 16:53
  • 1
    What is the difference between `vars` and `__dict__`? – mrgloom Sep 19 '19 at 11:18
  • `vars` **does *not* return class attributes**, only instance attributes (set by `__init__`) – Zack Plauché May 23 '21 at 19:48
  • best answer, inherit the dict, implement the getter/setter is simplest. – Sunding Wei Mar 19 '23 at 06:00
242

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

Berislav Lopac
  • 16,656
  • 6
  • 71
  • 80
  • 7
    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. – tvt173 Apr 19 '17 at 18:19
  • 2
    Can you explain why it's more Pythonic? – Hugh W Jan 04 '19 at 17:04
  • 4
    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 Jan 05 '19 at 19:57
  • 7
    It still fails if your class uses `__slots__` though. – c z Sep 11 '19 at 08:42
  • 1
    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). – Berislav Lopac Sep 12 '19 at 13:48
  • 1
    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](https://stackoverflow.com/a/65469063/6117426); 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). – Anakhand Dec 27 '20 at 18:35
72

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
animuson
  • 53,861
  • 28
  • 137
  • 147
dF.
  • 74,139
  • 30
  • 130
  • 136
  • 1
    This code includes methods. Is there a way to exclude methods? I only need the object's fields. Thanks – Julio César Sep 14 '08 at 18:32
  • 1
    `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. – Ehtesh Choudhury Dec 23 '13 at 20:53
  • I made some tweaks to crudely recurs and ignore all errors to a depth here, thanks! https://gist.github.com/thorsummoner/bf0142fd24974a0ced778768a33a3069 – ThorSummoner Aug 13 '16 at 05:54
31

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('__'))
Julio César
  • 12,790
  • 10
  • 38
  • 45
  • 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)... – dF. Sep 14 '08 at 18:55
  • 1
    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. – quamrana Sep 14 '08 at 20:24
  • Nice, btw note this is for python2.7, for python3 relpace iteritems() with simply items(). – Morten Nov 07 '19 at 10:03
  • And what about `staticmethod`? It's not `callable`. – Alex Dec 10 '19 at 22:57
24

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))
Seaux
  • 3,459
  • 2
  • 28
  • 27
  • 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 Dec 10 '19 at 22:53
20

A downside of using __dict__ is that it is shallow; it won't convert any subclasses to dictionaries.

If you're using Python3.5 or higher, you can use jsons:

>>> import jsons
>>> jsons.dump(f)
{'bar': 'hello', 'baz': 'world'}
R H
  • 1,898
  • 1
  • 17
  • 14
15

To build a dictionary from an arbitrary object, it's sufficient to use __dict__.

This misses attributes that the object inherits from its class. For example,

class c(object):
    x = 3
a = c()

hasattr(a, 'x') is true, but 'x' does not appear in a.__dict__

minopret
  • 4,726
  • 21
  • 34
indentation
  • 9,895
  • 6
  • 21
  • 14
10

Python3.x

return dict((key, value) for key, value in f.__dict__.items() if not callable(value) and not key.startswith('__'))
hizbul25
  • 3,829
  • 4
  • 26
  • 39
9

Late answer but provided for completeness and the benefit of googlers:

def props(x):
    return dict((key, getattr(x, key)) for key in dir(x) if key not in dir(x.__class__))

This will not show methods defined in the class, but it will still show fields including those assigned to lambdas or those which start with a double underscore.

Score_Under
  • 1,189
  • 10
  • 20
8

vars() is great, but doesn't work for nested objects of objects

Convert nested object of objects to dict:

def to_dict(self):
    return json.loads(json.dumps(self, default=lambda o: o.__dict__))
Ricky Levi
  • 7,298
  • 1
  • 57
  • 65
7

I think the easiest way is to create a getitem attribute for the class. If you need to write to the object, you can create a custom setattr . Here is an example for getitem:

class A(object):
    def __init__(self):
        self.b = 1
        self.c = 2
    def __getitem__(self, item):
        return self.__dict__[item]

# Usage: 
a = A()
a.__getitem__('b')  # Outputs 1
a.__dict__  # Outputs {'c': 2, 'b': 1}
vars(a)  # Outputs {'c': 2, 'b': 1}

dict generates the objects attributes into a dictionary and the dictionary object can be used to get the item you need.

radtek
  • 34,210
  • 11
  • 144
  • 111
  • 1
    After this answer still not clear how to get a dictionary from an object. Not properties, but entire dictionary;) – maxkoryukov Nov 09 '17 at 10:23
6

In 2021, and for nested objects/dicts/json use pydantic BaseModel - will convert nested dicts and nested json objects to python objects and JSON and vice versa:

https://pydantic-docs.helpmanual.io/usage/models/

>>> class Foo(BaseModel):
...     count: int
...     size: float = None
... 
>>> 
>>> class Bar(BaseModel):
...     apple = 'x'
...     banana = 'y'
... 
>>> 
>>> class Spam(BaseModel):
...     foo: Foo
...     bars: List[Bar]
... 
>>> 
>>> m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])

Object to dict

>>> print(m.dict())
{'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}]}

Object to JSON

>>> print(m.json())
{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}

Dict to object

>>> spam = Spam.parse_obj({'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y2'}]})
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y2')])

JSON to object

>>> spam = Spam.parse_raw('{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}')
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')])
Reed Sandberg
  • 671
  • 1
  • 10
  • 18
4

Dataclass(from Python 3.7) is another option which can be used for converting class properties to dict. asdict can be used along with dataclass objects for the conversion.

Example:

@dataclass
class Point:
   x: int
   y: int

p = Point(10, 20)
asdict(p) # it returns {'x': 10, 'y': 20}
Surya Teja
  • 1,220
  • 8
  • 9
2

As mentioned in one of the comments above, vars currently isn't universal in that it doesn't work for objects with __slots__ instead of a normal __dict__. Moreover, some objecs (e.g., builtins like str or int) have neither a __dict__ nor __slots__.

For now, a more versatile solution could be this:

def instance_attributes(obj: Any) -> Dict[str, Any]:
    """Get a name-to-value dictionary of instance attributes of an arbitrary object."""
    try:
        return vars(obj)
    except TypeError:
        pass

    # object doesn't have __dict__, try with __slots__
    try:
        slots = obj.__slots__
    except AttributeError:
        # doesn't have __dict__ nor __slots__, probably a builtin like str or int
        return {}
    # collect all slots attributes (some might not be present)
    attrs = {}
    for name in slots:
        try:
            attrs[name] = getattr(obj, name)
        except AttributeError:
            continue
    return attrs

Example:

class Foo:
    class_var = "spam"


class Bar:
    class_var = "eggs"
    
    __slots__ = ["a", "b"]
>>> foo = Foo()
>>> foo.a = 1
>>> foo.b = 2
>>> instance_attributes(foo)
{'a': 1, 'b': 2}

>>> bar = Bar()
>>> bar.a = 3
>>> instance_attributes(bar)
{'a': 3}

>>> instance_attributes("baz") 
{}


Rant:

It's a pity that this isn't built into vars already. Many builtins in Python promise to be "the" solution to a problem but then there's always several special cases that aren't handled... And one just ends up having to write the code manually in any case.

Anakhand
  • 2,838
  • 1
  • 22
  • 50
1

If you want to list part of your attributes, override __dict__:

def __dict__(self):
    d = {
    'attr_1' : self.attr_1,
    ...
    }
    return d

# Call __dict__
d = instance.__dict__()

This helps a lot if your instance get some large block data and you want to push d to Redis like message queue.

coanor
  • 3,746
  • 4
  • 50
  • 67
  • 2
    `__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. – Berislav Lopac Jan 05 '19 at 20:03
0

PYTHON 3:

class DateTimeDecoder(json.JSONDecoder):

   def __init__(self, *args, **kargs):
        JSONDecoder.__init__(self, object_hook=self.dict_to_object,
                         *args, **kargs)

   def dict_to_object(self, d):
       if '__type__' not in d:
          return d

       type = d.pop('__type__')
       try:
          dateobj = datetime(**d)
          return dateobj
       except:
          d['__type__'] = type
          return d

def json_default_format(value):
    try:
        if isinstance(value, datetime):
            return {
                '__type__': 'datetime',
                'year': value.year,
                'month': value.month,
                'day': value.day,
                'hour': value.hour,
                'minute': value.minute,
                'second': value.second,
                'microsecond': value.microsecond,
            }
        if isinstance(value, decimal.Decimal):
            return float(value)
        if isinstance(value, Enum):
            return value.name
        else:
            return vars(value)
    except Exception as e:
        raise ValueError

Now you can use above code inside your own class :

class Foo():
  def toJSON(self):
        return json.loads(
            json.dumps(self, sort_keys=True, indent=4, separators=(',', ': '), default=json_default_format), cls=DateTimeDecoder)


Foo().toJSON() 
snakecharmerb
  • 47,570
  • 11
  • 100
  • 153
spattanaik75
  • 143
  • 9
0

Try:

from pprint import pformat
a_dict = eval(pformat(an_obj))
thetaprime
  • 356
  • 4
  • 11
0

Python 3.7+ in 2023

You can add the dataclass decorator to your class and define a custom JSON serializer, then json.dumps will work (and you can extend it to work with non-serializable attributes by providing a custom encoder to cls).

f=Foo()
json.dumps(f, cls=CustomJSONEncoder)

{"bar": "hello", "baz": "world", "modified": "2023-02-08T11:49:15.675837"}

A custom JSON serializer can be easily modified to make it compatible with any type that isn't natively JSON serializable.

from datetime import datetime
import dataclasses
import json


@dataclasses.dataclass # <<-- add this decorator 
class Foo():
    """An example dataclass."""

    bar: str = "hello"
    baz: str = "world"
    modified: datetime = Column(DateTime(timezone=True), default=datetime.utcnow)


class CustomJSONEncoder(json.JSONEncoder): # <<-- Add this custom encoder 
    """Custom JSON encoder for the DB class."""

    def default(self, o):
        if dataclasses.is_dataclass(o): # this serializes anything dataclass can handle  
            return dataclasses.asdict(o)
        if isinstance(o, datetime): # this adds support for datetime
            return o.isoformat()
        return super().default(o)

To further extend it for any non-serializable type, add another if statement to the custom encoder class that returns something serializable (e.g. str).

Salvatore
  • 10,815
  • 4
  • 31
  • 69
0

I think the best way is to use attribute __dict__ example:

class Foo:
    def __init__(self):
        # keys are initialized with
        # their respective values
        self.bar = 'hello'
        self.baz = 'world'
  
f = Foo()
print (f.__dict__) # {'bar': 'hello', 'baz': 'world'}


sqlAlchemy example:

async def getAssessments(client_id: UUID, db: Session = Depends(get_async_session)):
    evaluation_id_data = await getEvaluationId(client_id, db)
    evaluation_id = evaluation_id_data['data']
    evaluation_dimensions_data = await getEvluationDimensions(evaluation_id, db)
    evaluation_dimensions = evaluation_dimensions_data['data']

    assessment = []
    for dimension in evaluation_dimensions:
        print(f"\n\n dimension {dimension} \n\n")
        print(f"\n\n dimension dict {dimension.__dict__} \n\n")

Where dimension is DimensionsEvaluation model object

class DimensionsEvaluation(Base):
    __tablename__ = "dimensions_evaluation"
    id = Column(Integer, primary_key=True)
    dimension_id = Column(Integer, ForeignKey('dimension.id'))
    evaluation_id = Column(Integer, ForeignKey('evaluation.id'))
    questions = Column(String, nullable=False)
    selected = Column(Boolean, default=False)

the result is like so

 dimension <app.models.evaluation.DimensionsEvaluation object at 0x10d8def50> 

 dimension dict {'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x10d8def90>, 'selected': True, 'dimension_id': 2, 'evaluation_id': 6, 'id': 16, 'questions': '[152,153]'} 

Check this link for simple example: https://www.geeksforgeeks.org/get-a-dictionary-from-an-objects-fields/

DINA TAKLIT
  • 7,074
  • 10
  • 69
  • 74
  • Be aware that `__dict__` does not give you a dict, but a mappingproxy, which does not support item assignment. – OscarVanL Mar 31 '23 at 15:03
  • thank you for the information, for me I use this way to access item values of my model class only to assign I can directly assign to the item – DINA TAKLIT Mar 31 '23 at 15:24
0

Here is a solution that does not require any libraries and supports nested objects:

def is_json(obj: object):
    return obj is None or type(obj) in {bool, int, str, list, dict}

def attrs(obj: object):
    return {
        name: getattr(obj, name)
        for name in dir(obj)
    }

def props(obj: object, max_depth: int=1, depth: int=0):
    if depth > max_depth:
        return {}

    return {
        name: attr if is_json(attr) else props(attr, max_depth, depth+1)
        for name, attr in attrs(obj).items()
        if not name.startswith('__') and not callable(attr)
    }
Marduk
  • 982
  • 8
  • 12