Peter Holliday
Peter Holliday

Reputation: 65

Create class instance with unknown number of attributes

This has been taxing me for some time, so it's possible what I am trying to do is not a good thing, so I need either the solution to my problem or a recommendation of how to do it right.

I have a spreadsheet full of data which I need to transform. The spreadsheet can have 140 or more columns, and I want to make this dynamic, so if someone changes the columns in the spreadsheet it still works. So my spreadsheet might look like this:

THISFIELDTHATFIELDTHIRDFIELD
       ABCD          123           Text
       EFGH          456    Other text

etc (man, making a table here is HARD!)

This works fine, and I am guessing you know there is a but coming:

class MyThing:
    pass

    def __init__(self, **kwargs):
        for attribute, value in kwargs.iteritems():
            setattr(self, attribute, value)

    def validate_my_thing(self):
        self.ATTRIBUTE = whatever I want to do with that attribute

If I instantiate MyThing like this:

new_thing = MyThing(THISFIELD="ABCD", THATFIELD=123, THIRDFIELD="Text")

It works perfectly. So the problem is I want to take the attribute names from the spreadsheet (which I can do, I don't need to know how to read a spreadsheet), and populate the attribute values from the columns in the spreadsheet. If I could instantiate the class like this:

new_thing = Mything({"THISFIELD": "ABCD", "THATFIELD": 123, THIRDFIELD: "TEXT"})

then my life would be easy.

Just to re-iterate, instantiating the class like:

new_thing = MyThing(THISFIELD = worksheet_cell(1,2))

won't work, like I said there might be 140 attributes, and I don't want the instantiation to be static, it has to be dynamic.

Upvotes: 1

Views: 113

Answers (2)

Holt
Holt

Reputation: 37606

Either change your constructor to:

def __init__ (self, fields):
    for attribute, value in fields.items():
        setattr(self, attribute, value)

Or call it with **:

new_thing = Mything(**{"THISFIELD": "ABCD", "THATFIELD": 123, THIRDFIELD: "TEXT"})

Note: If one of the keys is not a valid attribute name (e.g. 1), the first method will fail in setattr while the second will fail before calling the constructor.

Upvotes: 1

tckmn
tckmn

Reputation: 59273

Something like this should work:

def __init__(self, fields):
    for attribute, value in fields.iteritems():
        setattr(self, attribute, value)

All I've done is changed **kwargs to fields in the argument list (because you're passing in an actual dict).

Upvotes: 2

Related Questions