Confusing class behavior in python

Question:

I don’t understand the behavior of arrays in classes when looping over over them in an array of objects.

Ok, let’s say i have this code:

class example:
    arr = []
    def __init__(self):
        self.arr.append(69)

examples = []
for i in range(5):
    examples.append(example())
for e in examples:
    for a in e.arr:
        print(str(a))

Expected output is five 69s, but it prints 25 of them. Why?

EDIT: I’ve somehow figured out that all the instances use the SAME ARRAY, how is that possible if i used self?

Answers:

example.arr does only have 5 elements in it:

print(example.arr)

… but you’re iterating over example.arr 5 times (once for every one of the 5 example objects in the examples list)

Answered By: Alexander

example has a list-valued class attribute, so all instances of example share that same list. (You never assigned to self.arr, so you did not create an instance attribute that shadows the class attribute.) If you had created one instance of example, then example.arr would have one element. If you had created 50 instances of example, then example.arr would have 50 elements.

As a resut, example.arr contains 5 elements, because you created 5 instances of example. (There aren’t 5 objects each with a singleton list specific to it.) Your nested loop prints the full contents of a 5-element list 5 times, resulting in 25 lines of output.

What you likely intended was for your class to be defined as follows:

class Example:
    def __init__(self):
        self.arr = []
        self.arr.append(69)
        # Which would be the long way of writing
        # self.arr = [69]

Now the code

examples = []
for i in range(5):
    examples.append(example())

would give you a list of objects whose arr attribute each contains a single element, and

for e in examples:
    for a in e.arr:
        print(str(a))

would iterate over the distinct singleton list of each object, producing the expected 5 lines of output.

Answered By: chepner

You are appending 69 to arr 5 times while simultaneously appending the class to examples list. If you try this:

class example:
    arr = []
    def __init__(self):
        self.arr.append(69)
        print(self.arr)

You can see that your arr already has 69 5 times.

Then, when you append the example class to your list it keeps on appending the class, however simultaneously the example class keeps on updating. Your current examples list outputs this:

[<__main__.example object at 0x000001A2026EFAF0>, <__main__.example object at 0x000001A2026EFAC0>, <__main__.example object at 0x000001A2026EFA90>, <__main__.example object at 0x000001A2026EF8B0>, <__main__.example object at 0x000001A2026EF730>]

This whole list contains of the example class which has been lately updated to the arr containg 69 5 times.
To make your code functional with OOP try this:

class example:
    arr = []
    def __init__(self):
        self.arr.append(69)

examples = []
for i in range(5):
    example.arr = []
    examples.append(example())
for e in examples:
    for a in e.arr:
        print(a)

Edit: Thanks to @chepner for the clarification of OP’s requirement.

Answered By: The Myth

As you are updating the class variable each time, the arr variable in class appends the number, every time it is called. To avoid the behaviour, try the below if this was your intention.

    class example:
            #arr = []
            def __init__(self):
                    self.arr = []
                    self.arr.append(69)  ## now that the self.arr is defined it acts as object variable, else it will try to find the variable in the class.
    
    examples = []
    for i in range(2):
            examples.append(example())
    
    for e in examples:
            for a in e.arr:
                    print(str(a))

Please find the below reference, hope this helps:
https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables

Answered By: user548279
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.