Scott N
Scott N

Reputation: 113

chained class methods overwriting each other when only one is set

I have created chained methods across a set of classes, but when I set values for the last link in the chain, it is overwriting value in what I thought were separate instances within the class chain.

For example:

sub0.one.some.a.time_t.raw = 1
sub0.one.some.b.time_t.raw = 2

But:

print sub0.one.some.a.time_t.raw

returns 2 (not 1, as set above)

print sub0.one.some.b.time_t.raw

returns 2

Code is below. Sub0 is the top level in the chain, Sub4 is the lowest. I add instances of classes as methods within the class by using the type() method.

Why isn't sub0.one.some.a.time_t.raw returning 1, as I've set it? Isn't it a separate instance of all classes in the chain?

class Sub4(object):
    pass

class Sub3(object):
    pass

class Sub2(object):
    pass

class Sub1(object):
    def __init__(self, config):
        self.config_field = config['flag']

class Sub0(object):
    def __init__(self, config):
        self.config = config


sub4_methods = ['raw']
Sub4 = type("Sub4", (Sub4, ), { sub4:None for sub4 in sub4_methods})

sub3_methods = ['time_t']
Sub3 = type("Sub3", (Sub3, ), { sub3:Sub4() for sub3 in sub3_methods})

sub2_methods = ['a', 'b']
Sub2 = type("Sub2", (Sub2, ), { sub2:Sub3() for sub2 in sub2_methods})

sub1_methods = ['all', 'some']
Sub1 = type("Sub1", (Sub1,), { sub1:Sub2() for sub1 in sub1_methods})

configs = {
    "one": { "flag": True },
    "two": { "flag": False },
    }
Sub0 = type("Sub0", (Sub0,), { k:Sub1(v) for (k,v) in zip(configs.keys(), configs.values())})

#create class instance
sub0 = Sub0(configs)

#set values
sub0.one.some.a.time_t.raw = 1
sub0.one.some.b.time_t.raw = 2

#both of these return 1, even though they are set to 1 & 2 respectively
print sub0.one.some.a.time_t.raw
#returns 1

print sub0.one.some.b.time_t.raw
#returns 1

Thanks!

Upvotes: 0

Views: 60

Answers (1)

user2357112
user2357112

Reputation: 281011

I add instances of classes as methods within the class by using the type() method.

That is not what type does. type makes an entirely new class. Even if you're aware of that (you don't seem to be), it's a bizarre and unsuitable choice for what you're trying to do.

Isn't it a separate instance of all classes in the chain?

No. All objects in this attribute chain are shared.

If you want Sub0 instances to have Sub1 objects as attributes and Sub1 objects to have Sub2 objects as attributes, etc., do it the normal way, with __init__:

class Sub0(object):
    def __init__(self):
        self.one = Sub1({"flag": True})
        self.two = Sub1({"flag": False})

class Sub1(object):
    def __init__(self):
        self.all = Sub2()
        self.some = Sub2()
...

If you want to define your attribute names with lists, you can do that using setattr in __init__:

class Sub1(object):
    def __init__(self):
        for name in ['all', 'some']:
            setattr(self, name, Sub2())

Upvotes: 2

Related Questions