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.
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?
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.