[Python Series 5] Split Code with Functions and Modules

한국어 버전

As your control-flow code grows, functions let you split logic into named blocks, and modules let you separate files for reuse and testing. We'll move from basic function syntax to parameters, return values, and module imports step by step.

Key terms

  1. Parameter: The named placeholder in a function definition that receives input values.
  2. Module: A single Python file whose functions can be imported elsewhere.
  3. Type hint: A notation such as name: str that expresses the expected type for variables or function I/O.
  4. if __name__ == "__main__": A guard that runs code only when the file is executed directly, not when imported.

Core ideas

Study memo

  • Time required: 60 minutes
  • Prereqs: Basic conditionals/loops plus a uv-backed project
  • Goal: Define functions and reorganize logic into modules for reuse

Functions give logic a name; modules divide functionality by file. Together they make repeated calculations reusable and easier to test.

Code examples

Function definition basics

def greet(name: str) -> str:
    return f"Hello, {name}!"

message = greet("Jimin")
print(message)

Functions accept inputs via parameters and return values with return. Add type hints to clarify intent.

Default and keyword arguments

def notify(message: str, urgent: bool = False):
    prefix = "[URGENT] " if urgent else "[INFO] "
    print(prefix + message)

notify("Deployment complete")
notify("Error detected", urgent=True)

Defaults keep call sites short, and keyword arguments (urgent=True) increase readability.

Variadic arguments

def combine(*values: str, separator: str = ", ") -> str:
    return separator.join(values)

result = combine("A", "B", "C", separator=" | ")
print(result)

*values captures any number of positional inputs as a tuple. Use **kwargs to capture key–value pairs as a dictionary when needed.

Split functions into modules

Project structure example:

python-playground/
├─ app.py
└─ helpers/
   └─ reports.py

Define functions inside helpers/reports.py:

# helpers/reports.py

def summarize_scores(scores: list[int]) -> dict:
    total = sum(scores)
    average = total / len(scores)
    return {"total": total, "average": round(average, 2)}

Import them in app.py:

from helpers.reports import summarize_scores

scores = [82, 75, 91]
report = summarize_scores(scores)
print(report)

Splitting modules makes testing and reuse easier. Any script can import the same helper without duplicating code.

🧱 What counts as a module? Every Python file is a module. Group related behavior in its own file, then import it wherever you need the functionality.

Practical example: layered report generator

# services/reports.py
def create_summary(records: list[dict]) -> dict:
    total = len(records)
    completed = sum(1 for record in records if record["done"])
    return {"total": total, "completed": completed}


# cli/report_command.py
from services.reports import create_summary

def render_report(records: list[dict]) -> str:
    summary = create_summary(records)
    return f"{summary['total']} tasks total, {summary['completed']} completed"

Placing core logic in services and handling I/O in CLI modules keeps test scopes clean and splits team responsibilities (data processing vs. presentation).

Module responsibility map

cli/report_command.pyI/O and renderingservices/reports.pyData summarizationdata/CSV · JSONtests/test_reports.pyUnit tests imports functionsreads/writes filesvalidates logic

Visualizing these paths helps you decide where new functionality belongs as the project grows.

if __name__ == "__main__":

Run code only when the module executes directly:

def main():
    print("Running main logic")


if __name__ == "__main__":
    main()

When another file imports this module, main() will not run automatically, preventing accidental side effects.

Why it matters

Functions make logic testable; modules make collaboration easier. Guarding entry points with if __name__ == "__main__" prevents accidental execution when importing the same file elsewhere.

Practice

  • Follow along: Create helpers/summarize_scores.py, reproduce the function, and import it from app.py.
  • Extend: Add a display function like render_report in a separate file and call it from main().
  • Debug: Intentionally misspell a module path to trigger ModuleNotFoundError, then fix it with the correct relative path or an __init__.py.
  • Definition of done: At least two files import each other's functions, and the execution entry lives inside an if __name__ == "__main__" block.

Wrap-up

Functions and modules let you organize code by responsibility, making testing and reuse straightforward. This structure shines once you start handling files, JSON, and external data—which is exactly what we'll cover next.

💬 댓글

이 글에 대한 의견을 남겨주세요