11.6. The main() Event

The last new skill for this chapter is to learn why we need to add a main() function to our programs and modules. The best way to explain this is to examine what happens when we do NOT include main().

The program below uses two files—main.py and the module random_select.py.

11.6.1. A Sample Program With One Module

Let’s pretend that we want to create a program that gives us information about student scores. We started by coding the find_average_score and the create_report functions. Now we want to test these functions by sending in lists of numbers.

Notice that we first import the required module. Next, we define the find_average_score and create_report functions. Finally, we include four statements to call the functions and print some output.

In lines 20 - 21, we call find_average_score with a list of numbers with a known average (3) and then print the result.

In line 23, we create a list of 8 random scores with values ranging from 90 - 100. In line 24, we call create_report and send it the pretend_scores list so we can check the format of the returned string.

Example

Let’s see how well our program works. Run the code as-is and examine the output.

Wait… what? The output contains three lists that we don’t expect from our code we wrote in lines 20 - 24.

  1. Comment out lines 20 - 24, then re-run the program.

  2. Wait… what? The two print statements are commented out, but output still appears in the console!

11.6.2. Loose Statements

OK, we have a problem with our program, but it’s not with main.py. Click on the Files icon in the top-left corner of the editor. This will show the names of the two files that make up this program.

Click on random_lists.py to show its code in the editor.

The module contains five functions, but the issue is at the bottom of the file. Scroll to the end of the code to find the 8 statements in lines 44 - 51. We call these loose statements because they are not part of any function.

Sometimes loose statements are just leftover commands that we used to test some of the functions. However, most of the time, the loose code is needed to run the program. It could prompt the user for information, call the functions in the proper order, set up a required drawing space, etc.

In this case, the loose code contains three print statements, and these create the unexpected output. This brings up a very important point:

Loose statements ALWAYS run, even if none of the functions in the program get called.

In main.py, by running import random_lists on line 1, the loose code in the module executes. This is why we see three lists appear in the console even if we remove all print commands in main.py.

We need to learn how to deal with loose statements so that they only run when we want them to.

11.6.3. Add a main() Function

In our example, the loose code in random_lists is just there for testing. We could simply remove it, but let’s deal with it in a better way.

Loose code winds up in a program because it does not fit into any of the defined functions. However, these statements DO relate to each other because they serve the same purpose—they control the main job of the program.

Update the code in random_lists by defining a main() function before the loose code. Next, indent the loose lines so that they move into the function:

44
45
46
47
48
49
50
51
52
def main():
   numbers = create_random_number_list(8, -10, 10)
   no_repeat_numbers = create_unique_number_list(5, 0, 10)
   letters = create_random_letter_list(8)
   numbers.sort()
   no_repeat_numbers.sort()
   print(numbers)
   print(no_repeat_numbers)
   print(letters)

main() requires no parameters, nor does it need a return statement.

Return to main.py and uncomment lines 20 - 24. Click Run and notice that the extra output disappears! Since the loose code from random_lists is now inside its own function, it will only run if we call main().

Try It!

In main.py, add the statement random_lists.main() and re-run the program. The three extra lists should appear.

11.6.3.1. Add main() to main.py

Let’s do the same thing to wrap up the loose code in main.py:

20
21
22
23
24
25
def main():
   average = find_average_score([3, 1, 5])
   print(average)

   pretend_scores = random_lists.create_random_number_list(5, 90, 100)
   print(create_report(pretend_scores))

The following editor contains the updated code, with a main() function present in both files.

Example

Most of the work happens inside the function, but we still need the last four lines of code to get things done. They perform the main job of the program (printing a score report).

Run the program again to see how it behaves now:

Drat! Now NO output appears!

Since we placed the loose code into the main() function, those statements will not run unless we call the function.

In main.py, add one more statement to the bottom of the code:

20
21
22
23
24
25
26
27
def main():
   average = find_average_score([3, 1, 5])
   print(average)

   pretend_scores = random_lists.create_random_number_list(5, 90, 100)
   print(create_report(pretend_scores))

main()

Now when we run the code, it behaves as we want. Lines 20 - 25 tell Python, OK, we are defining a function called ‘main()’, and it holds all of the statements that control how the program behaves.

Line 27 tells Python, Please run the ‘main()’ function.

11.6.4. Yes, You Really Want to Add main()

In many programming languages, like Java and C#, we are not allowed to leave loose statements sitting at the bottom of our code. Python is more forgiving, but as a best practice we should keep ALL statements inside of a defined function.

Why?

New coders often think that adding main() provides little benefit, but they really, really, REALLY should. The reasons have everything to do with avoiding bugs from loose statements:

  1. Using main() prevents running unwanted code when we import a module.

  2. The main() function helps organize our code by putting all of the setup and control statements in one place. For example, in a turtle program, main() would be the place to define new turtles, set their properties, change the window appearance, and call drawing functions.

  3. Using main() helps prevent shadowing of variables.

    Examples

    If we define a list variable in a loose statement, then every function in the program can access that list.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    def add_item():
       a_list.append(9)
    
    def flip_list():
       a_list.reverse()
    
    a_list = [2, 4, 6]
    add_item()
    flip_list()
    print(a_list)
    

    Console Output

    [9, 6, 4, 2]
    

    If we define a list variable in main(), then it is harder to accidentally change it in another function. We have to send the list in as an argument before the other function can act on it.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    def add_item():
       a_list.append(9)
    
    def flip_list():
       a_list.reverse()
    
    def main():
       a_list = [2, 4, 6]
       add_item()
       flip_list()
       print(a_list)
    
    main()
    

    Console Output

    File "main.py", line 2, in add_item
       a_list.append(9)
    NameError: name 'a_list' is not defined
    

11.6.5. Check Your Understanding

Question

The main() function can ONLY be added to the main.py file.

  1. True
  2. False

Question

What is the purpose of the main() function? Select ALL that apply.

  1. To tie up loose code statements.
  2. To control the main tasks that a program performs.
  3. To import modules.
  4. To keep the program from running.
  5. To complete any setup needed to get the program started.
  6. To keep control statements from running if the code is imported as a module.