50

I have a class where I want to get the object back as a dictionary, so I implemented this in the __dict__(). Is this correct?

I figured once I did that, I could then use the dict (custom object), and get back the object as a dictionary, but that does not work.

Should you override __dict__()? How can you make it so a custom object can be converted to a dictionary using dict()?

HAL9000
  • 3,562
  • 3
  • 25
  • 47
code base 5000
  • 3,812
  • 13
  • 44
  • 73

3 Answers3

51

No. __dict__ is a method used for introspection - it returns object attributes. What you want is a brand new method, call it as_dict, for example - that's the convention. The thing to understand here is that dict objects don't need to be necessarily created with dict constructor.

thule
  • 4,034
  • 21
  • 31
  • 13
    I prefer this answer over the most voted. It's nice to know how `dict` works, but overriding `__iter__` to obtain a dict from an object looks a little hacky to me. It may also have some unintended ramifications. – Renan Ivo Jun 29 '15 at 19:07
  • I wouldn’t call `__dict__` a method for introspection. It is a descriptor on the class and it’s use goes well beyond introspection; e.g. giving you direct write access to the attribute namespace of an instance in your code when you have overriden `__setattr__` for example. – Martijn Pieters Jan 14 '20 at 23:44
48

__dict__ is not a special method on Python objects. It is used for the attribute dictionary; dict() never uses it.

Instead, you could support iteration; when dict() is passed an iterable that produces key-value pairs, a new dictionary object with those key-value pairs is produced.

You can provide an iterable by implementing a __iter__ method, which should return an iterator. Implementing that method as a generator function suffices:

class Foo(object):
    def __init__(self, *values):
        self.some_sequence = values

    def __iter__(self):
        for key in self.some_sequence:
            yield (key, 'Value for {}'.format(key))

Demo:

>>> class Foo(object):
...     def __init__(self, *values):
...         self.some_sequence = values
...     def __iter__(self):
...         for key in self.some_sequence:
...             yield (key, 'Value for {}'.format(key))
... 
>>> f = Foo('bar', 'baz', 'eggs', 'ham')
>>> dict(f)
{'baz': 'Value for baz', 'eggs': 'Value for eggs', 'bar': 'Value for bar', 'ham': 'Value for ham'}

You could also subclass dict, or implement the Mapping abstract class, and dict() would recognize either and copy keys and values over to a new dictionary object. This is a little more work, but may be worth it if you want your custom class to act like a mapping everywhere else too.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • seems logical, so if you wanted to pass a select set of information back for __iter__ could you use a return {"var1":"val1", ..."varn" : "valn"} instead of yield? – code base 5000 Apr 23 '14 at 18:37
  • @josh1234: No, `__iter__` *must* return an iterator; a dictionary is not itself an iterator. – Martijn Pieters Apr 23 '14 at 18:40
  • @josh1234: and you'd need to produce a sequence of key-value pairs. So `iter(somedictionary.items())` is fine, but not `somedictionary` directly. – Martijn Pieters Apr 23 '14 at 18:40
  • @josh1234: the other alternatives are to *subclass* `dict` or to otherwise implement the [`Mapping` abstract type](https://docs.python.org/2/library/collections.html#collections.Mapping). – Martijn Pieters Apr 23 '14 at 18:41
5

The following method will allow an object of the class to be casted to a dict:

def __iter__(self):
    for key in self.__dict__:
        yield key, getattr(self, key)
mehmet
  • 7,720
  • 5
  • 42
  • 48
  • This answer was the clearest for me. In my case I supplied a list of keys rather than relying on `self.__dict__` – mlibby May 23 '21 at 04:54