Changing the value of a nested dictionary changes other nested dictionary values

Question:

I started making a Python Discord bot in the library of Pycord. I wanted to make a bot that will work on multiple servers and each server will have different values. So, to do this I made a dictionary that will store all these values within a nested dictionary. However, when I tried to change the value of one nested dictionary, it changes the values in the other nested dictionaries.

Code:

server_ids = {}
default_server_vals = {'beetle_game_started': False, 'beetle_message_id': None,
'beetle_message_channel': None, 'beetle_player_1': None, 'beetle_player_2': None, 'beetle_game_on': False, 'player1_list' : [], "player2_list":[]}

@bot.event
async def on_ready():
    print('logged in')
    for i in bot.guilds:
        global server_ids
        global default_server_vals
        server_ids[str(i.id)] = default_server_vals
    print(server_ids)  # Generate server IDS
@bot.event
async def on_guild_join(guild):
    server_ids[str(guild.id)] = default_server_vals

@bot.slash_command(guild_ids=testing_servers, name="beetle", description="2 Player game")
async def beetle(ctx):
    print(server_ids[str(ctx.guild.id)].get('beetle_game_on'), server_ids[str(ctx.guild.id)].get('beetle_game_started'))

    if server_ids[str(ctx.guild.id)].get('beetle_game_on') == False and server_ids[str(ctx.guild.id)].get('beetle_game_started') == False:
        await ctx.respond("Game starting! React to join.")
        game_start_embed = discord.Embed(title="React to join beetle game! (2 Players Only)",
                                         colour=discord.Colour.green())
        game_start_embed.add_field(name="GAME RULES", value="""There are two players. There is one dice! The first player to finish the beetle drawing wins.
    Rolling a 1 – Body

    Rolling a 2 – Head

    Rolling a 3 – A leg

    Rolling a 4 – An eye

    Rolling a 5 – An antenna

    Rolling a 6 – The tail
    The first player to roll all 6 wins. However, the head and body must be drawn first to draw the other beetle parts.""")
        message = await ctx.send(embed=game_start_embed)
        await message.add_reaction(" ")
        print(ctx.guild.id)
        server_ids[str(ctx.guild.id)]['beetle_game_started'] = True
        print(server_ids)
        server_ids[str(ctx.guild.id)]['beetle_message_id'] = message.id
        server_ids[str(ctx.guild.id)]['beetle_message_channel'] = message.channel
    elif server_ids[str(ctx.guild.id)].get('beetle_game_started'):
        await ctx.respond("Someone already started a game! Try and join them.")
    else:
        await ctx.respond("There is already a beetle game playing!")

on_ready takes the server IDS that the bot is already in, and put it in a global server_ids variable. Then the nested dictionary is given as the server ID value for the dictionary. But, when I try to edit a value of a nested dictionary (inside the beetle slash command) it changes all of the other nested values.

For example, when I try to change the nested dictionary value of beetle_game_started it prints this:

{'912361242985918464': {'beetle_game_started': True, 'beetle_message_id': None, 'beetle_message_channel': None, 'beetle_player_1': None, 'beetle_player_2': None, 'beetle_game_on': False, 'player1_list': [], 'player2_list': []}, '938245167880753202': {'beetle_game_started': True, 'beetle_message_id': None, 'beetle_message_channel': None, 'beetle_player_1': None, 'beetle_player_2': None, 'beetle_game_on': False, 'player1_list': [], 'player2_list': []}}

It somehow changes the value of both nested dictionaries of the server IDS (the value of ‘beetle_game_started’)
How would I change the value of one nested dictionary without changing the value of others?

Asked By: Blue Robin

||

Answers:

The reason you see this behavior is because in Python dictionaries are mutable objects.

Pulling the relevant sections from the code provided

default_server_vals = {'beetle_game_started': False, 'beetle_message_id': None,
'beetle_message_channel': None, 'beetle_player_1': None, 'beetle_player_2': None, 'beetle_game_on': False, 'player1_list' : [], "player2_list":[]}

and

server_ids[str(i.id)] = default_server_vals

The server_ids[str(i.id)] assignment is just creating new references to a single dictionary. This is why when you change the value for one id, you observe the change for all other ids.

One way to get around this is to make a copy. I suggest taking a look at Python’s copy library.

Since default_server_vals contains lists as values, you’ll want to consider deepcopy.

Answered By: somebody3697
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.