Search code examples
pythonarraysobject

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_entrys.

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.


Solution

  • 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.