Simplified Dictionary Additions with "setdefault()" Method

Beginner-friendly Coding Challenge at the End!

Although I have now completed courses on using python to access web data and working with databases, I’m still working on locking down by skills with utilizing Lists and Dictionaries. This post is going to be about displaying and updating dictionaries.

One task I am trying to get down to the point where I can write the code without looking up examples for is adding items from a list or string to a dictionary. This is useful for counting occurrences of a character or string in a set of items.

A specific use case for something like this would be to update an item inventory in an RPG type game, where the player is going to be frequently picking up loot from downed enemies or found treasure chests. What items you have and how many of those items is crucial for a variety of things you might do in an RPG game. That inventory information is likely saved in the background as either a dictionary or a database file, but when the player looks in their item bag to craft something at a workbench, to check their ammo reserves, or to use for transactions at a trading post, that dictionary needs to be called and presented in a way that is nice for viewing. There are probably a lot of ways you could approach this, but the simplest way I came up with was what is shown below:

# Inventory Dictionary
inv = { 'rope': 1, 'gold coin': 42, 'torch': 6, 'dagger': 1, 'arrow': 12}

# Inventory display function
def displayInventory(inventory):
    print('Inventory:')
    item_total = 0 
    for k, v in inventory.items():
        print(str(v), k)
        item_total = item_total + int(v)
    print('Total number of items: ' + str(item_total))

displayInventory(inv)

This program gives an output of:

the important part of the function is in the “for” loop that iterates through the keys and values of the dictionary and prints each tuple as string values. In my case, I wanted to print the number of items before the name of the item, which is why i opted to write my print statement as print(str(v), k) , with ‘v’ representing the value and ‘k’ representing the item name. If you wanted the item names to come first, all you’d have to do is change it to print(k, str(v)) and the output would look like:

This was part of a coding exercise in a book I am working through called Automate the Boring Stuff With Python (by Al Sweigart).

Coming up with that function was the easy part of the exercise.

Part two was to create a function to update the inventory dictionary with new items from a list. This function would be what is called whenever the player searches the body of a downed enemy or comes across a loot chest and has the option to pick up items and add them to their inventory.

One thing I am finding about myself as I learn to code is that i tend to gravitate towards “if” and “else” statements to accomplish most of my goals. The function I came up with that worked was this:

# Example List representing items in a loot box
treasureChest = ['arrow', 'arrow', 'arrow', 'gold coin']

#inventory updating function that takes everything from a list
def addToInventory(inventory, addedItems):
    for i in addedItems:
        if i in inventory:
            inventory[i] += 1   # Handling for existing key strings
        else:
            inventory[i] = 1    # Handling for new key strings
    return inventory

to test the code, called the displayInventory() first (before calling the addToInventory() function) so we can see the inventory before and after the treasureChest[] contents were added. Then I update the inv{} dictionary with the treasureChest[] list and call the display function again.

print('Starting Inventory')
displayInventory(inv)
inv = addToInventory(inv, treasureChest)
print('\nInventory after found items')
displayInventory(inv)

The output looks like:

you can see that the counts of ‘arrow’ and ‘gold coin’ are updated in the “inventory after found items” section.

While the function I came up with to update the inventory dictionary is perfectly serviceable, it turns out there is a method that makes this even easier, called setdefault(). Its a default function in Python, so you don’t need to import any special libraries. It also gets rid of the need for the clunky if: and else: components of the for loop in the function.

This code:

def addToInventory(inventory, addedItems):
    for i in addedItems:
        if i in inventory:
            inventory[i] += 1   # Handling for existing key strings
        else:
            inventory[i] = 1    # Handling for new key strings
    return inventory

is shortened to:

def addToInventory(inventory, addedItems):
    for i in addedItems:
        inventory.setdefault(i, 0) # checks inventory key values against list item ad adds key if i is new
        inventory[i] += 1          # adds 1 to the value for key matching i  
    return inventory

It cuts out two lines from the function, which isn’t much, but I’m all for shortening things wherever possible.

An enhancement to this function would be to have it print what is being added to the inventory. the way to do this with the cleanest output would probably involve creating a temporary dictionary within the addToInventory() function and have it print that temporary dictionary using a function very similar to the displayInventory() function above (maybe this would actually have been a great use case to make displayInventory() a class rather than a function, as it would only need very slight modifications to work as an object within the addToInventory() function. I haven’t done much with classes and object oriented programming yet, so maybe this is something I can return to and enhance later. For now, I just went ahead and added one line into the ‘for’ loop to print(f’{i} added to inventory’).

The new function looks like this:

def addToInventory(inventory, addedItems):
    for i in addedItems:
        inventory.setdefault(i, 0)
        inventory[i] += 1
        print(f'{i} added to inventory')
    return inventory

Complete Program:

The full program now looks like:

# Inventory Dictionary
inv = { 'rope': 1, 'gold coin': 42, 'torch': 6, 'dagger': 1, 'arrow': 12}

# Inventory display function
def displayInventory(inventory):
    print('Inventory:')
    item_total = 0
    for k, v in inventory.items():
        print(str(v), k)
        item_total = item_total + int(v)
    print('Total number of items: ' + str(item_total) + '\n')

# List representing items in a loot box
treasureChest = ['arrow', 'arrow', 'arrow', 'gold coin']

#inventory updating function that takes everything from a list
def addToInventory(inventory, addedItems):
    for i in addedItems:
        inventory.setdefault(i, 0)
        inventory[i] += 1
        print(f'{i} added to inventory')
    return inventory

# Functions called to diplay outputs in terminal
print('Starting Inventory')
displayInventory(inv)
inv = addToInventory(inv, treasureChest)
print('\nInventory after found items')
displayInventory(inv)

The output looks like:

The first section is the starting inventory dictionary.

The middle section is done automatically with the print() line inside of the addToInventory() function.

The last section shows the updated inventory after the new items are added.

Explanation for setdefault():

The way the setdefault() method works is to retrieve a value for a given key (first argument), and insert that key if it doesn’t already exist in the dictionary. The integer value in the second argument sets the starting value applied when a new key is entered. We set 0 because the next line of the code will update it to 1 with the += 1 trick, and this avoids the need for the if: and else: statements to handle existing keys and new keys differently.

A Challenge for Readers:

I want to try something here. I don’t know if this blog is getting any visibility at all, but if any other beginners like myself are coming across this post, I have a challenge.

A frequent obstacle that players have to work around in RPG games is some sort of limit to the number items they can carry, or maybe the weight they can carry. Since we don’t have weight values defined in the item dictionary, we will just make a limit for the total item count for this challenge.

  1. Enable the user to set a max item count in their inventory with a global variable outside of either of the functions.

  2. Modify the addToInventory() function in such a way that it takes this max item count into effect and only adds items until the limit is reached. If the added list has enough items that adding all of it’s contents to the player’s inventory would run the player over their max item count, It should print a message stating that the limit was reached, and list the items that were not added.

  3. depending on what you set the max item count limit to, you may want to add enough items to the treasureChest[] list to trigger the ‘limiter’ code added to the addToInventory() function.

  4. Paste your modified code in the comments.

I haven’t attempted this challenge yet myself, but I have a pretty clear plan on how I will. I’m curious to see what others come up with though!

Additional Ideas for Program Enhancement:

There are many other functions that could be added to this program to make it more functional within a game, like giving the user the ability to select which items they pick up from a downed enemy or out of a loot box to get added to their inventory, leaving behind items they don’t want or need. In a real game, you would also want to make sure that when the items are added to the inventory dictionary, they are removed from the list at the same time to keep the player from simply visiting the same loot box repeatedly to “farm” certain items. To avoid this type of exploit, most RPG games implement some function preventing the item from existing in two places simultaneously. There is typically also be a function allowing the user to selectively remove items from their inventory to make room for more desirable items.

Wrap-up:

In conclusion, utilizing the setdefault() method in Python can significantly simplify the process of updating dictionaries, especially when dealing with tasks like managing an inventory in an RPG game. This method streamlines the code by eliminating the need for conditional statements, making it more efficient and easier to read. By understanding and applying such techniques, even beginners can enhance their coding skills and develop more robust programs. The challenge presented encourages further exploration and application of these concepts, fostering a deeper understanding of Python's capabilities. As you continue to learn and experiment, remember that simplifying your code not only improves performance but also enhances maintainability and readability.