ct_inc ct_inc - 2 months ago 14
Python Question

Python: Changing a single object within an array of objects changes all, even in a different array

I've got a one type of object,

data_entry
, that has a 2-dimensional array of other objects,
time_entry
.

Initialization of time_entries the array within
data_entry
looks like this:

[([time_entry()] * 12) for i in range(5)]


and initialization of the
data_entry
looks like this:

thing = data_entry()


Now, I have a list of "things", each which contains it's own 2d array of
time_entry
s.

Each
time_entry
has a list as one of it's attributes, initialized like so:

attributes = []


I modify attributes by extending it using
.extend()
.

However, the problem I run into when I do this is EVERY single
time_entry
object in EVERY single
data_entry
object gets extended.

I know problems like this can arise from improper initialization of objects, so I'm wondering if perhaps my object creations are poor or there is another python quirk I am unaware of.

Answer

If you are performing the initialization on the class, it will affect all instances of the class. If that’s the case, this is not a result of it being in a list, but of it being on the class. For example:

#!/usr/bin/python

class BedrockDenizen():
    attributes = []


wilma = BedrockDenizen()
fred = BedrockDenizen()

wilma.attributes.extend(['thin', 'smart'])
fred.attributes.extend(['fat', 'stupid'])

print 'Wilma:', wilma.attributes
print 'Fred:', fred.attributes

You will see that both Fred and Wilma are thin, smart, fat, and stupid.

Wilma: ['thin', 'smart', 'fat', 'stupid']

Fred: ['thin', 'smart', 'fat', 'stupid']

One way to fix this is to put the attribute creation into the init method, so that the attribute is per-instance:

class BedrockDenizen():
    def __init__(self):
        self.attributes = []

With that change, only Wilma is thin and smart, and only Fred is fat and stupid.

Wilma: ['thin', 'smart']

Fred: ['fat', 'stupid']

You may also need to show us more code. @Bakuriu notes that the problem may be that you are only creating one instance, and he may be right. For example, if this is closer to your code:

class BedrockDenizen():
    def __init__(self):
        self.attributes = []

neighborhood = [([BedrockDenizen()] * 2) for i in range(2)]

flintstones, rubbles = neighborhood
fred, wilma = flintstones

wilma.attributes.extend(['thin', 'smart'])
fred.attributes.extend(['fat', 'stupid'])

print 'Wilma:', wilma.attributes
print 'Fred:', fred.attributes

Then Fred and Wilma will continue to have the same attributes, because they aren’t really separate people. You may wish to use code more like this:

class BedrockDenizen():
    def __init__(self):
        self.attributes = []

neighborhood = [[BedrockDenizen() for n in range(2)] for i in range(2)]

flintstones, rubbles = neighborhood
fred, wilma = flintstones

wilma.attributes.extend(['thin', 'smart'])
fred.attributes.extend(['fat', 'stupid'])

print 'Wilma:', wilma.attributes
print 'Fred:', fred.attributes

That depends on what your needs are, though, as it seems like an odd way of doing things without more info.

Comments