Defining and Calling Functions

Introduction: Why Functions Exist

Welcome to Defining and Calling Python Functions! This is the very first unit of the course, so we begin at the foundation: teaching Python to remember a block of code under a name we choose.

Imagine a reporting script that prints a decorated welcome banner in three different places. Without functions, we type those same three print lines three separate times. Later, when the wording changes, we have to hunt down all three copies and edit each one, hoping we do not miss any.

A function solves this by letting us store a block of code once, attach a name to it, and run it whenever we want. In this lesson, we build a small program with a print_banner function that frames a welcome message. Along the way, we focus on the single idea this unit hinges on: defining code and running code are two completely separate events.

Anatomy of a Function Definition

Every function starts with a header line. Let's look at ours before adding anything else:

Python
def print_banner():

That short line carries four required pieces, in this exact order:

  • def: the keyword that announces "a function definition begins here";
  • print_banner: the name we will use later to run this code;
  • (): empty parentheses, which declare that this function has no parameters, so callers do not pass it arguments (parameters come later in this path);
  • :: the trailing colon, which tells Python that an indented block follows.

None of these parts are optional. Writing def print_banner: without parentheses, or def print_banner() without the colon, stops the program with a SyntaxError before anything runs. Python is strict here on purpose: the header is a contract, and it must be complete.

One nuance about those parentheses: empty parentheses declare that the function takes no parameters, so nothing is passed in when we call it. That is not the same as saying the function can never use outside information — a no-parameter function could still read a global value or ask the user for input — but print_banner happens to rely on nothing external at all.

The Indented Body

The header alone does nothing useful; it needs a body, which is the code the function will run. In Python, indentation is what marks that body, rather than braces or an end keyword:

Python
def print_banner():
    print("=" * 30)
    print("Welcome to the Report Tool")
    print("=" * 30)

All three print lines are indented one level (four spaces is the standard convention), and they must be indented consistently with each other. The body continues for as long as the indentation continues, and it ends the moment a line returns to column 0.

Notice "=" * 30: multiplying a string repeats it, so this produces a rule of exactly thirty = characters. That keeps the framing line tidy without typing thirty symbols by hand.

Defining Is Not Running

Here is the part that surprises many learners: if we run a file containing only the definition above, the console stays completely empty. Not one = appears.

When Python reads a def block, it does not execute the body. It packages those three print statements together, attaches the package to the name print_banner, and then moves on to the next line at column 0. The body is now stored, waiting.

Think of writing a recipe card. Writing down "boil water, add pasta, drain" does not produce dinner; it just records the steps for later. A silent program like this one is not broken; it is simply a program that stored some code and never asked for it to run.

Calling The Function

To actually run the stored body, we call the function by writing its name followed by parentheses. Here is our complete program:

Python
# Define a function: this stores the code but does not run it yet
def print_banner():
    print("=" * 30)
    print("Welcome to the Report Tool")
    print("=" * 30)


# Call the function: now the body actually runs
print_banner()

Three rules make that final line work: the name must match the definition exactly, the parentheses are required (writing print_banner alone evaluates the name and runs nothing), and the call must come after the def so the name already exists. Critically, the call sits at column 0, which makes it part of the script rather than part of the body. Running this prints:

text
==============================
Welcome to the Report Tool
==============================

Tracing The Execution Order

Let's follow the file exactly as Python does, from top to bottom. First, it reads the comment and skips it. Then it reads def print_banner():, stores the three-line body under that name, and prints nothing at all. It skips straight over the indented lines without running them. Next, it reaches print_banner() at column 0, jumps into the stored body, runs the three print statements in order, then returns to the call line and continues with the rest of the file, which is empty here.

Execution flow showing that the definition stores the function body and the call runs it

All three output lines therefore come from that one call, never from the def block. A quick "what if" makes the point stick: indent the call by four spaces, and it becomes part of the body itself, so the only thing that could trigger it is the function already running. Nothing calls it, and the program falls silent again.

Naming Functions Well

Since the name is how we run the code later, it deserves some care. Python's convention is snake_case: lowercase words joined by underscores. For functions, we also prefer starting with a verb that says what the function does, such as print_banner, print_footer, or show_menu.

Compare that with weaker choices:

NameProblem
PrintBannerCapWords is conventionally used for class names, so snake_case is preferred for functions
pbToo short to explain anything to a future reader
bannerReads like a piece of data, not an action

A few hard rules also apply: names cannot contain spaces, cannot start with a digit, and should avoid reusing built-in names like print or list, since redefining those quietly takes away the original tool for the rest of the program.

Conclusion And Next Steps

Let's gather the four takeaways: a definition starts with def name():, including the colon; the code it stores lives in a consistently indented body; a matching name() call at column 0 is what actually runs that body; and storing code is a separate event from running it. Notice also the compact style we used, with no main() wrapper and no if __name__ == "__main__": block, so the execution order reads straight down the page. That flat layout is a deliberate teaching simplification: calling functions directly at module level keeps the execution order easy to follow while we concentrate on defining and calling. Larger programs usually gather these calls inside a main() function guarded by if __name__ == "__main__":, a convention this path returns to later.

Next up are the practices, where we will add the missing call to a silent script, write a brand-new function from scratch, and play detective on a function whose call is trapped inside its own body. Let's go make some code actually run!

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal