Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

*sorry if the title doesn’t make sense I’m not sure how to word it

So 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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

testing_servers = [912361242985918464, 938245167880753202]
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)
@bot.event
async def on_guild_join(guild):
    server_ids[str(guild.id)] = default_server_vals

@bot.slash_command(guild_ids=testing_servers, name="test", description="Test out bot latency")
async def test(ctx):
    await ctx.respond(f"Bot Latency: {bot.latency * 100}")


@bot.slash_command(guild_ids=testing_servers, name="eightball", description="Play 8ball with friends")
async def eightball(ctx, question):
    eightball_int = random.randint(1, 5)
    response = None
    if eightball_int == 1:
        response = "I don't quite know"
    if eightball_int == 2:
        response = "Well no."
    if eightball_int == 3:
        response = "Yes of course!"
    if eightball_int == 4:
        response = "Maybe it's best not to answer."
    if eightball_int == 5:
        response = "Bruh Moment."
    embed = discord.Embed(title="Eightball", description=f"""You asked: {question}
My response: {response}""", colour=discord.Colour.green())
    await ctx.respond(embed=embed)


@bot.slash_command(guild_ids=testing_servers, name="roll_dice", description="Roll a dice!")
async def roll_dice(ctx, sides: int):
    dice_int = random.randint(1, int(sides))
    embed = discord.Embed(title="Dice", colour=discord.Colour.green())
    embed.add_field(name="You rolled a:", value=str(dice_int))
    embed.add_field(name="Dice sides:", value=str(sides))
    await ctx.respond(embed=embed)


@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!")

What this does in on_ready is take 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? Thanks.

>Solution :

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 https://docs.python.org/3/library/copy.html

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading