quantum_reaper
quantum_reaper

Reputation: 3

python3 list creation from class makes a global list rather than a series iterated ones

So here is the problem I am having. I am trying to iterate the makeAThing class, and then create a list for the iteration using the makeAList class. Instead of making seperate lists for each iteration of makeAThing, it is making one big global list and adding the different values to it. Is there something I am missing/don't know yet, or is this just how python behaves?

class ListMaker(object):    

    def __init__(self,bigList = []):
        self.bigList = bigList



class makeAThing(object):    

    def __init__(self,name = 0, aList = []):
        self.name = name
        self.aList = aList

    def makeAList(self):
        self.aList = ListMaker()




k = []
x = 0
while x < 3:
    k.append(makeAThing())
    k[x].name = x
    k[x].makeAList()
    k[x].aList.bigList.append(x)
    x += 1    

for e in k:
    print(e.name, e.aList.bigList)

output:

0 [0, 1, 2]
1 [0, 1, 2]
2 [0, 1, 2]


the output I am trying to achieve:
0 [0]
1 [1]
2 [2] 

After which I want to be able to edit the individual lists and keep them assigned to their iterations

Upvotes: 0

Views: 39

Answers (1)

robert_x44
robert_x44

Reputation: 9314

Your init functions are using mutable default arguments.

From the Python documentation:

Default parameter values are evaluated from left to right when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified. This is generally not what was intended. A way around this is to use None as the default, and explicitly test for it in the body of the function, e.g.:

def whats_on_the_telly(penguin=None):
    if penguin is None:
        penguin = []
    penguin.append("property of the zoo")
    return penguin

In your code, the default argument bigList = [] is evaluated once - when the function is defined - the empty list is created once. Every time the function is called, the same list is used - even though it is no longer empty.

The default argument aList = [] has the same problem, but you immediately overwrite self.aList with a call to makeAList, so it doesn't cause any problems.

To verify this with your code, try the following after your code executes:

print(k[0].aList.bigList is k[1].aList.bigList)

The objects are the same.

There are instances where this behavior can be useful (Memoization comes to mind - although there are other/better ways of doing that). Generally, avoid mutable default arguments. The empty string is fine (and frequently used) because strings are immutable. For lists, dictionaries and the sort, you'll have to add a bit of logic inside the function.

Upvotes: 1

Related Questions