“Modules and Imports” Exercise

To setup this exercise, bring up an existing repository, or create a new repository called “imports-practice”. Navigate there from the command line, and open the repository using your text editor.

In the following example, we are setting up two files in the repo’s “app” subdirectory: one called “utils.py” containing some code that will be imported, and another called “script.py” which will import and use that code.

In the “utils.py” file, we are defining a simple utility function called enlarge. If we weren’t concerned with code modularity, we could invoke that function on the left margin, in the program’s global scope, and that is valid:

# this is the "app/utils.py" file (v1)...

# FUNCTION DEFINITION:

def enlarge(n):
    return float(n) * 100

# FUNCTION INVOCATION:

x = input("Please input a number")
result = enlarge(x)
print(result)

Enabling Code to be Imported

However, if we are concerned with code modularity, and we want to be able to not only use the enlarge function in this file, but also import it for usage in in other files, this current approach is problematic. It turns when we try to import some code from a given file, Python actually executes the entire contents of the file’s global scope. In this case, the importing file would not be able to complete it’s import - it would be hung up, waiting for a user input.

Instead, to enable modularity, we must reorganize the code, introducing a Python convention called the “main conditional”. We leave any constants and function definitions and class definitions in the global scope, while nesting any remaining code inside the “main conditional”, to prevent it from being executed during an import.

# this is the "app/utils.py" file (v2)...

# FUNCTION DEFINITION:

def enlarge(n):
    return float(n) * 100

if __name__ == "__main__":

    # FUNCTION INVOCATION:

    x = input("Please input a number")
    result = enlarge(x)
    print(result)

The weird “main conditional” prevents anything nested inside it from being executed when we import code from this file. However it still also allows us to execute the code nested inside, when we run the file from the command-line.

Running the file from the command-line:

# filepath-based run command:
python app/utils.py

# alternative, modular-style run command:
python -m app.utils

Importing Code

With the “main conditional” in place, we can import the enlarge function cleanly, for example into another file called “script.py”:

# this is the "app/script.py" file...

# IMPORTING FUNCTION FROM ANOTHER FILE:

from app.utils import enlarge

# FUNCTION INVOCATION:

result = enlarge(9)
print(result)

Notice, when we import code from a file, we reference that file using dot notation, like app.utils, where “app” is the directory name, and “utils.py” is the file name. Notice, we omit the trailing “.py” from the file name in this approach.

Because this file imports some code from another file, we actually have to run it an alternative way from the command-line, using this same dot notation and a “modular style” command with a -m flag.

Running the file from the command-line:

python -m app.script

Alright, we were able to import code from one file to another! This helps us organize the codebase in a modular way.