0

I'm having a misunderstanding about python classes/variables when used in an array. Given the below example, I would expect each 'basket' to have only one 'apple'. Yet each one ends up with 5? Why is that and how can I fix my code?

class Apple:
    def __init__(self):
        return

class Basket:
    apples: list[Apple] = list()
    def add_apple(self, apple):
        self.apples.append(apple)

baskets = list()
for i in range(5):
    # I'm instantiating a new basket here, why do apples keep getting added to the 'old' baskets that are in the baskets array?
    b = Basket()
    a = Apple()
    b.add_apple(a)
    baskets.append(b)

for b in baskets:
    print(len(b.apples))
5
  • apples is a class variable - it is shared by all instances of the Basket class. You need to define it inside __init__ if you want each instance to have its own list. Relevant documentation Commented Jan 5, 2022 at 15:37
  • See Object-Oriented Programming in Python. Commented Jan 5, 2022 at 15:38
  • Can't find duplicate, but it must exist. Here's article about mutable function arguments: docs.python-guide.org/writing/gotchas/… In short, apples is initialized once when class is created (class, not instance). So all Basket instances share the same apples list (because it's mutable). To fix this behavior, move this initialization code into __init__ method, it will make apples an instance attribute instead. Commented Jan 5, 2022 at 15:39
  • To see that difference, you can try assert baskets[0].apples is baskets[1].apples - it will not throw, so the list instances are exactly the same and all attributes point to the same object. Commented Jan 5, 2022 at 15:39
  • @chepner yeah noticed, i deleted it Commented Jan 5, 2022 at 15:39

4 Answers 4

3

You've defined Basket.apples as a class attribute, meaning it's shared by all Basket instances! Make it an instance attribute by defining it in __init__ and attaching it to the self instance:

class Apple:
    pass


class Basket:
    def __init__(self) -> None:
        self.apples: list[Apple] = []

    def add_apple(self, apple: Apple) -> None:
        self.apples.append(apple)

Each time you make a new Basket, Basket.__init__ is called on the new instance, and self.apples = [] is executed, creating a new list that belongs to the new instance. Now if you execute the rest of the code, you get the output:

1
1
1
1
1

because each basket contains one unique Apple.

Sign up to request clarification or add additional context in comments.

Comments

0

The variable apples is bound to the class not the instance of the class. You can change that via

class Basket:
    def __init__(self):
         self.apples =[]

Comments

0

Every instance of Basket is sharing the same class attribute apples. You want an instance attribute attached to each instance.

class Basket:
    apples: list[Apple]

    def __init__(self):
        self.apples = []

    def add_apple(self, apple):
        self.apples.append(apple)

The apples: list[Apple] is optional, only needed by a static type checker, not the class itself.

Comments

0

As a list is a mutable element, you chosed to append to self.apples. But you probably did not realized that self.apples was a reference the a class variable. So for each basket, you added an apple to the same list... And the 5 basket indeed shared that common list.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.