Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a custom conversion from an object to a dict?

Tags:

python

I have a Python class that stores some fields and has some properties, like

class A(object):
  def __init__(self, x, y):
    self.x = x
    self.y = y

  @property
  def z(self):
    return self.x+1

What changes do I need to make to the class so that I can do

>>> a = A(1,5)
>>> dict(a)
{'y':5, 'z':2}

where I specify that I want to return y and z? I can't just use a.__dict__ because it would contain x but not z. I would like to be able to specify anything that can be accessed with __getattribute__.

like image 934
murgatroid99 Avatar asked Jun 06 '12 13:06

murgatroid99


2 Answers

Add an __iter__() method to your class that returns an iterator of the object's items as key-value pairs. Then you can pass your object instance directly to the dict() constructor, as it accepts a sequence of key-value pairs.

def __iter__(self):
    for key in "y", "z":
        yield key, getattr(self, key)

If you want this to be a little more flexible, and allow the list of attributes to be easily overridden on subclasses (or programmatically) you could store the key list as an attribute of your class:

_dictkeys = "y", "z"

def __iter__(self):
    for key in self._dictkeys:
        yield key, getattr(self, key)

If you want the dictionary to contain all attributes (including those inherited from parent classes) try:

def __iter__(self):
    for key in dir(self):
        if not key.startswith("_"):
            value = getattr(self, key)
            if not callable(value):
                yield key, value

This excludes members that start with "_" and also callable objects (e.g. classes and functions).

like image 102
kindall Avatar answered Sep 22 '22 13:09

kindall


I think a reasonable approach to this problem would be to create an asdict method. When you say you want to specify which keys you want the dict to contain, I assume you're happy with that information being passed when the method is called. If that's not what you mean, let me know. (This incorporates kindall's excellent suggestion.)

class A(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @property
    def z(self):
        return self.x+1

    def asdict(self, *keys):
        if not keys:
            keys = ['y', 'z']
        return dict((key, getattr(self, key)) for key in keys)

Tested:

>>> A(1, 2).asdict('x', 'y')
{'y': 2, 'x': 1}
>>> A(1, 2).asdict()
{'y': 2, 'z': 2}
like image 23
senderle Avatar answered Sep 19 '22 13:09

senderle