user1159454
user1159454

Reputation: 3317

Why is this list within lists assignment repeating itself? Classes

Using Python, and classes, I'm trying to make a game like battleship, mostly for practice. So I made a game Board object, and you "see" it, and "change" it. (I'll merge "create" in with init later)

But something weird is happening where my "Change" is applying to every row...here is the code:

class Board:
    'Game Board'
    topMarkers = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    sideMarkers = list(range(0, 26))


    def __init__(self,h,w): #Makes the map
        self.height = h
        self.width = w


    def create(self): #Makes a map for displaying
        wave = '~'
        self.row = []
        self.column = []
        #self.column = wave * self.width # If width is 4, column is now '~~~~'

        for c in range(self.width):
            self.column.append(wave)


        raw_input(self.column)

        for r in range(self.height): #
            self.row.append(self.column)

        raw_input(self.row)

    def showGrid(self):

        print self.row

    def changeRow(self, y, x):
        self.row[1][2] = "Test"







yourShipsMap = Board(4,3)

theirShipsMap = Board(4,7)


theirShipsMap.create()

theirShipsMap.changeRow(2,2)
theirShipsMap.showGrid()

When I run this, it's writing "Test" in not just the first list, second index, but in every list, second index. Any idea why it's doing that??

I hate when I stop programming all together for like a month and forget everything.

Upvotes: 0

Views: 138

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1123400

You create only one column, then append it to your rows. Appending a list to another list does not make a new copy, but re-uses the same list over and over.

Append a copy instead by using the [:] slice syntax or a list() call:

for r in range(self.height): #
    self.row.append(self.column[:])

Here is an example of what happens for you:

>>> column = [1, 2]
>>> row = []
>>> row.append(column)
>>> row.append(column)
>>> column[0] = 'foobar'
>>> row
[['foobar', 2], ['foobar', 2]]

If we use a copy on the other hand, the copies do not change when we alter the original:

>>> row = []
>>> row.append(list(column))
>>> row.append(column[:])
>>> row
[['foobar', 2], ['foobar', 2]]
>>> column[0] = 1
>>> row
[['foobar', 2], ['foobar', 2]]

I used both methods of creating a (shallow) copy of the original columns list in the above example. If you need to create a deep copy (a list containing other mutable items such as lists or dicts), use the copy.deepcopy utility instead.

Upvotes: 3

eumiro
eumiro

Reputation: 213005

for r in range(self.height): #
    self.row.append(self.column)

you keep appending the same list object, which will be seen in each row if updated in one.

Try this:

self.row = [['~'] * self.width for r in xrange(self.height)]

This will create a list of lists with all elements independent from each other.

Upvotes: 2

Related Questions