thedarklord47
thedarklord47

Reputation: 3302

How to implement __setitem__ to set attributes of a class?

my classes

class Base:
    #has no attributes of its own

    def __init__(self, params):
        for key in params:
            if hasattr(self, key):
                self[key] = params[key]

    def __setitem__(self, key, value):
        self[key] = value



class Child(Base):
    prop1 = None
    prop2 = None

This however, goes into endless recursion as self[key] = value recursively calls self.__setitem__

My goal is to be able to pass a a dictionary into a Child() constructor like this

params = dict(
    prop1 = "one",
    prop2 = "two"
)
c = Child(params)

c.prop1     #"one"
c.prop2     #"two"

There are many different classes like Child but with different fields. params is a dict from a json blob. I want to use Base as a generic populator for the different classes like Child

I have seen methods that use an internal dict to accomplish what I am asking but it is my understand (I am brand new to Python) that this would prevent accessing the methods via dot notation (which I would rather avoid).

Upvotes: 0

Views: 1831

Answers (2)

donkopotamus
donkopotamus

Reputation: 23176

Just update the __dict__ of the instance in your __init__:

class Base:
    def __init__(self, params):
        for key in params:
            if hasattr(type(self), key):
                self.__dict__[key] = params[key]

Then:

class Child(Base):
    field1 = None
    field2 = None

c = Child(dict(field1="one", field2="two", field3="three"))

print(c.field1)     # "one"
print(c.field2)     # "two"
print(c.field3)     # => attr error

Grandchildren will behave:

class GrandChild(Child):
    field3 = None

gc = GrandChild(dict(field1="one", field2="two", field3="three"))

print(gc.field1)     # "one"
print(gc.field2)     # "two"
print(gc.field3)     # "three"

Upvotes: 2

Ryan Haining
Ryan Haining

Reputation: 36802

The closest I can imagine to what you want involves using setattr, which takes an object, an attribute name (as a str) and a value for that attribute.

class Base(object):
    def __init__(self, params):
        for k, v in params.iteritems():
            if hasattr(self, k):
                setattr(self, k, v)

class Child(Base):
    def __init__(self, params):
        self.field1 = None  # create attributes here, not at class level
        self.field2 = None
        Base.__init__(self, params)

params = dict(
    field1 = "one",
    field2 = "two",
    field3 = "tree", # ignored when used with Child since it has no field3
)
c = Child(params)

Upvotes: 1

Related Questions