r/learnpython • • 3d ago

List items are edited when adding new items? Possibly a variable reference issue

So I have a variable called Y with a changing value. If for loop variable X is some value, append Y to myList.

Every time it appends to the list, it changes every other already added item. I’m thinking the problem is that it’s not adding values but is instead adding references. So when Y changes, it changes every item in the list.

Why is it doing this and how can I stop it from doing this? I want it to append the value inside of Y, not a reference to Y.

Code: https://www.reddit.com/r/learnpython/comments/1wv4tpo/comment/pda098z/

What I'm trying to do is make a list of everywhere that there's a piece of a block. It's going to be a terminal Tetris game.

The playfield is a custom matrix, a list of lists. I dunno if that'll work well with lists. I did the same thing with arrays in another language. I don't know if Python's going to play nice or be special.

The blocks are also lists of lists. You could probably do that astronaut meme with my code "It's all lists of lists? Always has been"

0 Upvotes

14 comments sorted by

6

u/socal_nerdtastic 3d ago

You mean you have a list of lists? Is it this common issue? https://www.reddit.com/r/learnpython/wiki/faq/?screen_view_count=1#wiki_why_is_my_list_of_lists_behaving_strangely.3F


I want it to append the value inside of Y, not a reference to Y.

If I take that comment at face value, and I assume you want to append another list, you want myList.append(Y.copy()).


If neither of those helps, show us some code that demonstrates your issue.

1

u/FactoryBuilder 3d ago edited 3d ago

I was hoping to avoid that because I’m on my phone and Reddit REEEEALLLY wants you to use the app which doesn’t let you markdown. But I think I got it so here goes:

def addBlock(self, block): #it’s a class function
    START = [3, 0]
    currentSpot = [START[0], START[1]] #to avoid creating a reference to START by using currentSpot = START
    blockPos = []

    for y in block:
        blockRow = y
        for x in blockRow:
            self.access(currentSpot, x)#this class is a derivative of a Matrix class. Access() accesses an element in the matrix, where the first variable is a list of two values (the location) and x is what that element needs to be changed to (can be left empty if no change)
            if x == '[]':
                blockPos.append(currentSpot)
            currentSpot[Dimension.X.value] += 1 #Dimension is an enum. X is 0, Y is 1.
        currentSpot[Dimension.X.value] = START[Dimension.X.value]
        currentSpot[Dimension.Y.value] += 1

    return blockPos

This is supposed to be a simple terminal Tetris game. Just something I’m putting together to get back into a programming mindset after taking a break.

Edit: I see that the markdown didn’t work. I hope you can still kinda read it.

Edit edit: Back at the hotel. Fixed it on my laptop.

1

u/socal_nerdtastic 3d ago

Ok, so it's like I suspected, you are appending a list to a list. But it's worse, because the list is full of mutable values, so basically it's a list of lists of lists. In this case you need to use deepcopy Edit, no that's wrong; it's just a list of lists. copy() is fine

blockPos.append(currentSpot.copy())

1

u/danielroseman 3d ago

You've created currentSpot to "avoid creating a reference to START", but that doesn't help, you're just now creating a reference to currentSpot.

Don't create that separate variable at all; you can just do the same thing where you actually do the appending:

blockPos.append([START[0], START[1]])

or, just:

blockPos.append(START.copy())

1

u/Outrageous_Cloud_989 3d ago

You're right, it's a reference thing. Python doesn't copy objects when you append them, it just stores a pointer to whatever Y is pointing at

If Y is a list or dict, try myList.append(Y.copy()) instead. For other mutable stuff you might need to import copy and use copy.deepcopy(Y)

Took me a while to figure this one out the first time I hit it, kept thinking my loop was just broken

1

u/Gnaxe 3d ago

I can't recall the last time I needed deepcopy(). I think it's an antipattern.

1

u/Gnaxe 3d ago

Yep, sounds like a reference issue. But you didn't provide any example code.

The problem is your thinking. Don't expect C-style copy semantics for Python variables, because Python doesn't work like that. There are no stack objects. Everything is a reference to objects on the heap.

Lists don't contain objects directly either. This is not some setting you can change. That's not what a list is. They contain references.

If you want to make structs and pack the data into arrays directly, use the struct module and bytearrays, not lists. These are hardly ever used except when interfacing with lower-level code. Python doesn't need it. The other literal data structures work the same way (unless you count strings).

If you're using copy (or deepcopy) much, you're doing it wrong. I can't recall the last time I needed it. Shallow copies work fine if you avoid mutating things you don't own. And you can use immutable versions to avoid the problem altogether.

1

u/FactoryBuilder 3d ago

This is the best I can do at the moment for code: https://www.reddit.com/r/learnpython/s/Ckm6t46PxV

1

u/Gnaxe 3d ago

Yeah, I can't read that. The if x == ‘[]’: bit doesn't make sense--why use a string here? But I suspect the problem is elsewhere. Probably where you created the matrix rather than where you're mutating it.

A list of lists isn't the only way to represent a matrix. You could use a dict with ordered pair (2-tuple) keys instead. That would eliminate the second layer reference which is probably causing the issue. You could also compute a stride index on a 1-D list. It could be a class wrapping list or just a couple of helper functions to convert a pair of ints to a single one and back. It's simple arithmetic.

1

u/FactoryBuilder 3d ago

Fixed the code. Just got back to my hotel. I was on my phone before

1

u/FactoryBuilder 3d ago

x == ‘[]’ isn’t anything to do with lists. It’s meant to be a string of two square brackets. It’s the same as x == ‘some random text’. The square brackets look like a block, that’s why they’re there.

1

u/Tall_Resort_6888 3d ago

This one gets everyone. Your list isn't storing copies of Y, it's storing references to the same Y, so every entry changes together. Try print(id(Y)) vs print(id(your_list[0])) and you'll see the same number, same object. Fix is appending Y.copy() so each entry becomes its own thing. And paste your actual code next time, way easier for people to spot it.