pythonpython-3.xpython-collections

Python append to list replacing all previous indexes with last value


In the following Python 3 code, the correct value is written into the daysSchedule but when iterating to the next value.


class Data:

    def getDaysSchedule(scheduleUrl, filterTeam = ''):
        with urllib.request.urlopen(scheduleUrl) as request:
            data = json.loads(request.read().decode())
            daysSchedule = GameList()
            for gameData in data["dates"][0]["games"]:
                game = Game()
                game.gameId = gameData["gamePk"]
                game.home.name = gameData["teams"]["home"]["team"]["name"]
                game.away.name = gameData["teams"]["away"]["team"]["name"]
               
                print('\n' + str(game.gameId)) # CORRECT VALUE HERE
                
                daysSchedule.games = daysSchedule.games[:] + [game]  # HERE HAVE TRIED APPEND BUT THAT DIDN'T WORK EITHER

                
                # DEBUG CODE
                menuItems = daysSchedule.games
                for testItem in menuItems:
                    testId = testItem.gameId
                    title = testItem.home.name + ' VS. ' + testItem.away.name
                    print(title)

            return daysSchedule

I get output from the debug chunk of code above like the following. Each one of this is a write out of one of the rows. And you will see a new row gets added with correct values, but all the previous rows incorrectly get overwritten with this value too.

633855
Chicago Cubs VS. San Diego Padres
Chicago Cubs VS. San Diego Padres

633834
Arizona Diamondbacks VS. New York Mets
Arizona Diamondbacks VS. New York Mets
Arizona Diamondbacks VS. New York Mets

633853
Baltimore Orioles VS. Minnesota Twins
Baltimore Orioles VS. Minnesota Twins
Baltimore Orioles VS. Minnesota Twins
Baltimore Orioles VS. Minnesota Twins

633882
New York Yankees VS. Tampa Bay Rays
New York Yankees VS. Tampa Bay Rays
New York Yankees VS. Tampa Bay Rays
New York Yankees VS. Tampa Bay Rays
New York Yankees VS. Tampa Bay Rays

What in the world is going on -- besides me obviously not having enough caffeine today.

Here is the Games class

class Game(object):
    gameId = '' 
    link = ''
    date = ''
    time = ''
    status = Status()
    home = Team()
    away = Team()
    series = Series()
    venue = Venue()

... and the GameList class:

class GameList(object):
    games = [Game()]

Solution

  • All the trouble came from the way you use classes. Please, note the difference:

    This:

    class Game(object):
        gameId = '' 
        link = ''
        date = ''
        time = ''
        status = Status()
        home = Team()
        away = Team()
        series = Series()
        venue = Venue()
    

    Vs.

    class Game():
        def __init__(self):
            self.gameId = '' 
            self.link = ''
            self.date = ''
            self.time = ''
            self.status = Status()
            self.home = Team()
            self.away = Team()
            self.series = Series()
            self.venue = Venue()
    

    In the former ones you have class attributes which are attributes common for all instances of this class. While the later ones are instance attributes, which are attributes for a particular instance.

    Knowing that, we now see how on each iteration the outer loop adds a new instance of the Game class to the instance of GameList, while being different objects (with different id(), memory locations), they all share thier class attributes, i.e. when the program accessed game.home.name it was the same for all instances of the Game class.

    It's like a global variable shared among all class instances that was modified on each iteration, despite creation (independent in any other sense) instances of the class.

    By the way, we can have both:

    class Foo:
        class_attr = 'I am shared among all the instances'
        def __init__(self):
            self.instance_attr = 'I belong only to a particular instance'
    

    In the second case here, the particular instance will be passed as self implicitly.