4

I've got essentially an elaborate wrapper around a list of dictionaries:

class Wrapper(object):
    def __init__(self, data):
        self.data = data

    def get(self, attr):
        return [d[attr] for d in self.data]

So,

Wrapper([{'x': 23}, {'x': 42}, {'x': 5}]).get('x')

returns [23, 42, 5]. Now I want to assign shorthand properties, so that Wrapper.x will return the same as Wrapper.get('x'). I don't know what keys are present in the data a priori, so my current approach is (adapted from this question:

class Wrapper(object):
    def __init__(self, data):
        self.data = data
        for key in data[0].keys():
            setattr(self, key, property(lambda self: self.get(key)))

So, assumption is that all elements of data have the same keys and they're all valid identifiers in python. But then, Wrapper(...).x returns <property at 0x10a3d4838> What am I doing wrong?

Community
  • 1
  • 1
Manuel Ebert
  • 8,429
  • 4
  • 40
  • 61
  • @Martijn Pieters - I think you should reconsider how you've marked as duplicate here. The article you link to is about *classes*, which in python for this particular topic is strangely very different from *instances*. – Jamie Marshall Mar 25 '19 at 23:44
  • @JamieMarshall: no, the OP wants there to be dynamic attributes based on the values in an instance. That doesn't preclude adding a `__getattr__` method to the class. The other post covers that option. – Martijn Pieters Mar 26 '19 at 11:16

1 Answers1

11

You can actually do this easily by just changing the name of your function:

>>> class Wrapper(object):
...     def __init__(self, data):
...         self.data = data
...     def __getattr__(self, attr):
...         return [d[attr] for d in self.data]
... 
>>> Wrapper([{'x': 23}, {'x': 42}, {'x': 5}]).x
[23, 42, 5]

The __getattr__() special method is called whenever you request an attribute that doesn't exist. The only potential issue here is that you could override this by assigning an attribute. If you need to avoid that, simply override __setattr__() as well to stop that from happening.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183