All Іѕ Vаиітy
All Іѕ Vаиітy

Reputation: 26412

how do I create a dict with all fields and properties of a python object?

Suppose if I have a class as shown below,

class TestClass(object):

    def __init__(self):
        self.name = 'Marty'
        self.age = 25
        self.place = 'CL'

    @property
    def country(self):
        return 'USA'

    @property
    def age_group(self):
        return '18-25'

I want to create a dict out of all the @properties. I have tried with __dict__ and vars but still the @property is not showing up. How can I include @property too.

>>> x = TestClass()
>>> x.__dict__
{'age': 25, 'name': 'Marty', 'place': 'CL'}
>>> vars(x)
{'age': 25, 'name': 'Marty', 'place': 'CL'}

I want to include age_group and country with the returned values as the keys and values.

Upvotes: 3

Views: 143

Answers (2)

PM 2Ring
PM 2Ring

Reputation: 55469

You can get the names you want by calling dir on your instance. However, the list returned by dir also has a bunch of names you don't want (the names of the methods inherited from object), so we need to filter those out.

class TestClass(object):
    def __init__(self):
        self.name = 'Marty'
        self.age = 25
        self.place = 'CL'

    @property
    def country(self):
        return 'USA'

    @property
    def age_group(self):
        return '18-25'

x = TestClass()
d = {k: getattr(x, k) for k in dir(x) if not k.startswith('__')}
print(d)

output

{'age': 25, 'age_group': '18-25', 'country': 'USA', 'name': 'Marty', 'place': 'CL'}

Here's a cleaner way to do this. We loop over the vars() of the class object, specifically looking for items that are property instances. Then we use itertools.chain to combine those names with the names from calling vars() on the instance of the class.

from itertools import chain

class TestClass(object):
    def __init__(self):
        self.name = 'Marty'
        self.age = 25
        self.place = 'CL'

    @property
    def country(self):
        return 'USA'

    @property
    def age_group(self):
        return '18-25'

    def do_stuff(self):
        return 'hello'

def attributes_and_properties(obj):
    props = (k for k, ktype in vars(type(obj)).items() 
        if isinstance(ktype, property))
    return {k: getattr(obj, k) for k in chain(props, vars(obj))}

x = TestClass()
print(attributes_and_properties(x))

output

{'country': 'USA', 'age_group': '18-25', 'name': 'Marty', 'age': 25, 'place': 'CL'}

This way is superior to the previous technique, since that code will also include bound methods whose names don't start with __, like do_stuff, in its output.

Upvotes: 3

Richard Neumann
Richard Neumann

Reputation: 3361

Use a dict comprehension that iterates over the objects attributes:

obj = TestClass()
object_dict = {attr: getattr(obj, attr) for attr in dir(obj)}

If you really just want the @property attributes, which is unclear in your question, you can filter for them like so:

object_properties_dict = {attr: getattr(obj, attr) for attr in dir(obj.__class__) if type(getattr(obj.__class__, attr)) is property}

Upvotes: 3

Related Questions