25. Modules and import — Homework solutions

Each problem has its own folder under exercises/25/homework/solutions/. To run one:

cd exercises/25/homework/solutions/01-greet-module
python main.py

Problem 1 — Greet module

Problem. Finish a greet.py module exposing hello and bye.

How to think about it. A plain .py file with two functions — no wrapper needed. Both are tiny: one print each.

Worked solution. greet.py:

def hello(name):
    print(f"Hello, {name}!")

def bye(name):
    print(f"Goodbye, {name}.")

main.py:

import greet

greet.hello("Keiko")
greet.bye("Keiko")

Common mistakes.

  • Adding if __name__ == "__main__": guards in the module and calling the functions there. The module should only define functions; the caller does the calling.
  • Running from the wrong directory. cd into the folder first, or Python will not find greet.py.

Problem 2 — Math helpers

Problem. A module with double and triple.

How to think about it. Same shape as Problem 1, but the functions return values instead of printing. The caller does the printing.

Worked solution. math_helpers.py:

def double(x):
    return x * 2

def triple(x):
    return x * 3

main.py:

import math_helpers as mh

print(mh.double(7))           # 14
print(mh.triple(7))           # 21
print(mh.double(mh.triple(5)))   # 30

Common mistakes.

  • Putting print inside the module functions. Returning values lets the caller decide what to do with them.

Problem 3 — Counter

Problem. A module with increment, get, and reset. The internal counter must persist across calls.

How to think about it. A variable at module level (outside any function) lives for the module's lifetime — which, thanks to Python's import caching, is the whole program's lifetime. All three functions read and modify that same variable. Functions that assign to a module-level variable must declare it with global.

Worked solution. counter.py:

_count = 0

def increment():
    global _count
    _count = _count + 1

def get():
    return _count

def reset():
    global _count
    _count = 0

main.py:

import counter

counter.increment()
counter.increment()
counter.increment()
print(counter.get())     # 3

counter.reset()
print(counter.get())     # 0

Common mistakes.

  • Declaring count inside increment. Every call resets it to 0, then bumps it to 1. Persistent state must live at module level, outside the function.
  • Forgetting global _count inside increment and reset. Without it, Python creates a local variable that shadows the module-level one; the module-level value never changes.

Challenge — String utilities

Problem. A module with three string functions of your own design.

How to think about it. Pick three. The example shows shout, echo, and reverse_words. reverse_words splits on whitespace with .split() and reverses the resulting list.

Worked solution. string_utils.py:

def shout(s):
    return s.upper() + "!"

def echo(s, n):
    return " ".join([s] * n)

def reverse_words(s):
    words = s.split()
    words.reverse()
    return " ".join(words)

main.py:

import string_utils as su

print(su.shout("hello"))                      # HELLO!
print(su.echo("hi", 4))                       # hi hi hi hi
print(su.reverse_words("one two three four")) # four three two one

s.split() with no argument splits on any whitespace and returns a list of words. [s] * n creates a list of n copies of s. " ".join(...) joins them with spaces.

Common mistakes.

  • Writing s.split(" ") instead of s.split(). The no-argument form handles multiple spaces and leading/trailing whitespace cleanly; the single-space form does not.

Done?

Part 5 is finished once the four homework programs run. The Part 5 mini-project — a Text Adventure — combines functions, lists, dictionaries, and modules into a tiny game world.