DEV Community

Cover image for Python Beginners Tutorial: Animal registry
Ricardo Aguilar
Ricardo Aguilar

Posted on

Python Beginners Tutorial: Animal registry

We will write a program that will allow us to make a register of animals, with their scientific name and common name, binded by a unique ID. Using that ID we will be able to remove the animal from the register, or simply modify the names already written. Also an option to view the list of animals already registered and an option to exit the program.

The goal is to generate and print a list that can be modified like this:

Animal Registry
ID Scientific Name Common Name
3Y99 Canis Lupus Familiaris Siberian Husky
MT5R Cebinae Capuchin Monkey
8D6U Octopoda Octopus
YG0E Anthophila Bee
JN14 Dinastinae Rhinoceros Beetle
WVTA Python Regius Ball Python
HAZP Gorilla Gorilla

We will work with:

  • The PrettyTable module.
  • Nested dictionaries.
  • Flow control.
  • Functions.

You must install PrettyTable via Pip.
Try with:
pip install prettytable

First, we will import the necessary modules:
PrettyTable for the creation of the table where we will register the animals, random and string to
generate a random ID with which we will identify the row in the record and sys to exit the program.

from prettytable import PrettyTable
import random
import string
import sys
Enter fullscreen mode Exit fullscreen mode

Now we will create an empty dictionary where we will store the animals that we are going to register.

animal_dic = {}
Enter fullscreen mode Exit fullscreen mode

We will write the conditional "if name == "main"" to avoid import errors and for good practice.
and call our main() function.

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

An explanation of how if _name_ == "_main_" works is beyond the scope of this article.


Functions:

Now, let's write the functions that will run our program, these will go BEFORE the conditional "if _name_ == "_main_"" described above. Otherwise Python would try to execute the main() function when it has not been defined, thus triggering an error.


Main function:

Now, we must bind the option chosen by the user to the functions of our program.

The next step is straightforward: inside main() we will force this loop to run, with a while True,
it will print the instructions to add, delete, update, check and exit the program.
We also have an input() that will be the option that the user chooses and this will be stored in
the variable user_input. Pay attention to the .lower() method, transform what the user enters into lower case, so we avoid typing errors.
Finally, the conditionals. Depending on what the user chooses, a function will be called.

# ~~~~~~~~~~~~~~~~~~ Functions(): ~~~~~~~~~~~~~~~~~~~
def main():
    # ~~~~~~~~~~~~~~ User's choise ~~~~~~~~~~~~~~~
    while True:
        instrucctions()
        user_input = input("\nWhat do you want to do? "
                           "(a, d, u, l, e): ").lower()
        if user_input == "a":
            add_animal()
        elif user_input == "d":
            delete_animal()
        elif user_input == "u":
            update_animal()
        elif user_input == "l":
            print_register()
        elif user_input == "e":
            exit_program()
        elif not user_input:
            print("please, enter something!")
Enter fullscreen mode Exit fullscreen mode

Instructions:

We will create a function so that the user can see the options of our program along with a key to press to execute that option, and so that the user knows what to do. The options are: add animal, delete animal, update animal, check list and exit the program.
So, it would be like this:

# ~~~~~~~~~~~~~ Functions ~~~~~~~~~~~~~~
def instrucctions():
    print('\nAnimal registry program:'
          '\n1: Enter A or a to add new animal.'
          '\n2: Enter D or d to delete a animal'
          '\n3: Enter U or u to update animal.'
          '\n4: Enter L or l to check list of animals. '
          '\n5: Enter E or e to exit the program.')
Enter fullscreen mode Exit fullscreen mode

We print the options. '/n' means a line break.


Print the list:

def print_registry():
    x = PrettyTable(["ID", "Scientific Name", "Common Name"])
    for animal_data in animal_dic:
        x.add_row([animal_data, animal_dic[animal_data]["scientific_name"],
                   animal_dic[animal_data]["common_name"]])

    print(x.get_string(title="Animal registry"))
Enter fullscreen mode Exit fullscreen mode

Pay attention to what happens here:

  • To give names to the column headers we write them in list format, and we pass it as parameter of PrettyTable and store it in the variable "x".
  • Before we talk about the for cycle, we must remember and be aware that we are trying to access a dictionary *within a dictionary. Just to lining things up, let's compare a common* dictionary, to a nested one.

Dictionary:

Where we have a collection of items stored in key/value pairs.

  {'name': 'Adam'}
Enter fullscreen mode Exit fullscreen mode

'name' is the key and 'Adam' is the value.

  {'name': 'Adam', 'last': 'Smith'}
Enter fullscreen mode Exit fullscreen mode

We can have more than one pair of key/value in just ONE dictionary. 'name' and 'last' are both keys.

Nested dictionary:

The values to the outside of the dictionary, are also dictionaries.

  {0: {'book': 'Wealth of Nations', 'author': 'Adam Smith'},
   1: {'book': 'Economic Sophisms',' author': 'Frédéric Bastiat'}}
Enter fullscreen mode Exit fullscreen mode

0 is the key of the external dictionary, and 'book' and 'author are keys of the internal dictionary.

With that in mind, let's go back to the previous code.

for animal_data in animal_dic:
Enter fullscreen mode Exit fullscreen mode

What happens here is that we access the external key, through animal_data.

x.add_row([animal_data, animal_dic[animal_data]["scientific_name"],
           animal_dic[animal_data]["common_name"]])
Enter fullscreen mode Exit fullscreen mode

Since we access the external key, we will have access to the internal values. It happens as follows:

  • animal_dic = Animal ID
  • animal_dic[animal_data]["scientific_name"] = scientific name
  • animal_dic[animal_data]["common_name"] = common name

So, then:

x.add_row([animal_data, animal_dic[animal_data]["scientific_name"],
           animal_dic[animal_data]["common_name"]])
Enter fullscreen mode Exit fullscreen mode

We enter the values of our dictionary into the row of PrettyTable as a list, by means of the
method add_row(). Look carefully, everything is between braces [].

Finally we give a header to our table:

print(x.get_string(title="Animal registry"))
Enter fullscreen mode Exit fullscreen mode

Now you can print the list by starting the program and pressing "l" or "L". Obviously, in this moment our dictionary is empty. We will see later how to enter data into it.


Random ID:

We need an ID to identify our rows, this ID will serve as a binding, being the external key of our nested dictionaries.
It is necessary, in the future, to be able to update one or both names of the animal, or to delete it definitively.
To make it easier for the user, and to avoid creating duplicates, we will create a function that returns
a random string. We will do it with the following code:

def random_id():
    random_string = ''.join(random.choices(string.ascii_uppercase
                                           + string.digits, k=4))
    return random_string
Enter fullscreen mode Exit fullscreen mode

Where "k" is the length of the string.

An explanation of how this function works is beyond the scope of this article.


Add:

Now let's make the function that will take the values typed by the user and enter them into the dictionary.

def add_animal():
    animal_id = random_id()
    scientific_name = input("\nPlease enter the scientific name: ").title()
    common_name = input("\nPlease enter the common name: ").title()
    data = {animal_id: {'scientific_name': scientific_name,
                        'common_name': common_name}}
    if not scientific_name and not common_name:
        print("You must write something!")
    else:
        animal_dic.update(data)
Enter fullscreen mode Exit fullscreen mode
  • The first thing you'll notice is that we call the random_id function which returns a string and store it in the animal_id variable. As the name implies, it identifies the animal and the row within the record.
  • Using the input() method we ask the user to type the common and scientific name. and store them in variables. We use the .title() method for aesthetics.

Let's pause and look at this part of the code again:

    data = {animal_id: {'scientific_name': scientific_name,
                        'common_name': common_name}}
Enter fullscreen mode Exit fullscreen mode
  • We update our nested dictionary and store it in a variable called "data". So:
    animal_id is the random ID that our function returns. It is the external key of this
    nested dictionary.
    'scientific_name': which is in quotation marks, is the internal key.
    scientific_name: which is a variable, is the internal value. Entered by the user.

  • The next block of code sets a condition where if the user does not enter either a scientific name or a common name, print a message that he/she must do so.

  • Finally, in the 'else': if at least one of the two input() have been entered, we will update the "animal_dic" with the data entered by the user.


Delete row:

Now let's write the function to delete the row of the user's choice, using the unique ID.

def delete_animal():
    animal_id = input("\nEnter the animal ID you want delete: ").upper()
    if animal_id in animal_dic:
        choice = input("Delete (y/n)").lower()
        if choice == "yes" or choice == "y":
            del animal_dic[animal_id]
            print(f"{animal_id} registry has been deleted!")
    else:
        print("ID not found. Check list pressing 'L'")
Enter fullscreen mode Exit fullscreen mode
  • We ask the user to enter the ID of the row. We use the * upper () * method so that the user does not have to type in all capital letters.
  • We create an 'if' that checks if the ID entered by the user is in the dictionary, then give him the choice to delete it with a yes or no. If user types yes it will be deleted with a "del animal_dic[animal_id]" which looks for the ID (external key) and deletes all its internal key/values.
  • With the else we declare that, if the ID has not been found, it prints "ID not found"

Update:

Perhaps the user has made a typo, and wants to change the fields in a specific row.
There is a need for a function to update them.

def update_animal():
    animal_id = input("\nEnter the animal ID you want update: ").upper()
    # If external key in dictionary, if key is equal to ID (animal_id)
    for animal in animal_dic:
        if animal == animal_id:
            choice = input(f"Update registry {animal_id}? (y/n): ").lower()
            if choice == "yes" or choice == "y":
                # Changing names
                scientific_name = input("Write a new scientific name: ").title()
                common_name = input("Write a new common name: ").title()
                if not scientific_name and not common_name:
                    print("You must write something!")
                else:
                # Updating
                    animal_dic[animal]['scientific_name'] = scientific_name
                    animal_dic[animal]['common_name'] = common_name
                    print("registry updated!")
                    print_registry()
        else:
            print("ID not found. Check list pressing 'L'")
Enter fullscreen mode Exit fullscreen mode
  • The user enters the ID via an input() and it is stored in a variable.
  • If the ID (external key) exists in our dictionary it will step through it with a for loop, otherwise, it will print an "ID not found".
  • We ask if the user really wants to delete the row and store their answer in a *variable. If yes, we proceed. Otherwise the flow stops.
  • We ask the user, using an input(), to type the new names and we store them in variables.
  • If the user has left both fields blank, it will print "You must write something!" otherwise we will access the dictionary, its foreign key and store the new values.

Exit the program:

def exit_program():
    sys.exit("Goodbye!")
Enter fullscreen mode Exit fullscreen mode

We use the exit() method of the built-in sys module to exit the program and print a message.


Full code:

https://github.com/RicardoRien/animal_registry

Top comments (0)