0

I'm really confused about the way this works. I have the following class:

class Square:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def move_to(self, direction):
        if direction == 'Right':
            self.x += 10
            
        if direction == 'Left':
            self.x -= 10
        
        if direction == 'Up':
            self.y -= 10
        
        if direction == 'Down':
            self.y += 10
            
    def __repr__(self):
        return f"Square[x={self.x}, y={self.y}]"

Then I make this list (two squares in (10, 0) and (20, 0)) and "move" the first square 10 steps to the left:

square_list = [Square(10, 0), Square(20, 0)]
print(square_list)

square_list[0].move_to("Left")
print(square_list)

All seems fine:

[Square[x=10, y=0], Square[x=20, y=0]]
[Square[x=0, y=0], Square[x=20, y=0]]

But when I set a list with one element and then append the first element to the same list to latter move only the first element:

square_list = [Square(10, 0)]
square_list.append(square_list[-1])
print(square_list)

square_list[0].move_to("Left")
print(square_list)

I have an unexpected behaviour (the two squares move to the left):

[Square[x=10, y=0], Square[x=10, y=0]]
[Square[x=0, y=0], Square[x=0, y=0]]

And that's because the two elements have the same IDs. Why??? I try to make an Snake Game algorithm shifting the elements of the snake body and then move the head/first element, but the only way this work is appending a new object Square doing square_list.append(Square(square_list[-1].x, square_list[-1].y). There's another cleaner way to doing this?

Luis Munoz
  • 146
  • 4
  • You append the last square on the list to the list. Now you have two references to the same square on the list, not two different squares. – DYZ Jul 01 '20 at 03:13
  • 1
    Because in the latter case you have a lists with *two referenes to the same object*, in the former case, you have a list with *two references to two different objects*. Note, every list is an *object list*. Everything in Python is an object. As to *why*, because `.append` doesn't implicitly copy the object it is appending to the list, it just appends that object. Check this out: `mylist = []; mylist.append(mylist)` – juanpa.arrivillaga Jul 01 '20 at 03:13
  • 1
    A cleaner way would be to create a `.copy` method in your `Square` class that simply returns a `Square` object with the same values for it's attributes, then you could do `square_list.append(square_list[-1].copy())` – juanpa.arrivillaga Jul 01 '20 at 03:15
  • Or, alternatively, make your `Square` class's API "immutable", and have all mutator methods actually return new `Square` objects. (of course, your `Square` objects will still be mutable, but not if you stick to the exposed API, which is true really about any object in python, even built-in immutables) – juanpa.arrivillaga Jul 01 '20 at 03:15
  • Thanks @juanpa.arrivillaga. I try the copy thing, but confusing with the copy method of list. – Luis Munoz Jul 01 '20 at 04:21

0 Answers0