Study guide

def, return, None, and Call Flow

PCEP Certified Entry-Level Python Programmer Study Guide Exam

Track your progress as you read

Finished sections get a check mark.

1 PCEP Exam Map and Study Strategy

Current PCEP-30-02 Facts How PCEP Tests Code Reading Official Resources and Ethical Prep Four-Week Study Plan and Readiness

2 Computer Programming and Python Fundamentals

1.1 Lexis, Syntax, Semantics, and Indentation 1.2 Identifiers, Keywords, Comments, and Basic Program Structure 1.3 Literals, Types, and Numeral Systems 1.4 Operators, Precedence, and Type Conversion 1.5 print(), input(), and Console Tracing

3 Control Flow

Boolean Expressions, Comparisons, and Truthiness If, Elif, Else, and Decision Trees While Loops and Sentinel Patterns For, Range, and Iteration Patterns Break, Continue, Pass, Loop Else, and Nesting

4 Data Collections

Indexing, Slicing, and Sequence Rules Lists, Mutation, Methods, and Aliasing Tuples, Immutability, and Packing Dictionaries, Keys, Membership, and Iteration Strings, Sets, and Collection Choice

5 Functions and Exceptions

def, return, None, and Call Flow Parameters, Arguments, Defaults, and Keywords Scope, Shadowing, global, and Local Errors Exception Types, try, except, else, and finally Recursion, Debugging, and Final Code Reading

def, return, None, and Call Flow

Key Takeaways Explain This Topic AI

A def statement creates a function object at definition time; the indented body does not run until the function is actually called.

A function that reaches the end of its body without executing a return statement returns None implicitly.

print() displays text on standard output and itself evaluates to None, while return sends a value back to the caller.

Once a return statement executes, the current function call ends and any later statements on that path are skipped.

Nested function calls are resolved innermost-first; the inner return value becomes the outer call's argument.

Last updated: June 2026

Why call flow matters on PCEP

The PCEP-30-02 exam asks 30 questions in 40 minutes (about 80 seconds each, 70% to pass). Functions-and-exceptions items rarely ask you to write code; they show a short snippet and ask what it prints, returns, or raises. To answer fast and correctly, separate three distinct moments: Python reads the def statement, the program calls the function, and the caller uses whatever value comes back.

A def statement binds a name to a function object . The indented body is compiled and stored, not executed. The body runs only when the name is followed by parentheses with the required arguments. def double(n):

  • print('inside')
  • return n * 2
  • print('before')
  • value = double(4)
  • print(value)

The output is before , then inside , then 8 . Defining double printed nothing. The assignment value = double(4) receives the return value 8 ; it does not receive the word printed inside the body.

return versus print: the most-tested distinction

  • Feature print() return
  • Main job Writes text to standard output Sends a value back to the caller
  • Value of the expression Always None The returned object
  • Can be stored/combined No (you store None ) Yes
  • Ends the function call No Yes, immediately
  • Common PCEP trap Assuming printed text is "the result" Code after return is silently skipped

A function may both print and return in one call, but the actions are unrelated. If a question asks for output , count only printed text. If it asks for the value assigned to a variable , follow the return value. def label(x):

print('item', x)

  • result = label(3)
  • print(result)

This prints item 3 and then None . label has no return statement, so when the body ends Python supplies None automatically, and that None is what result stores.

Three ways a function returns None

There are exactly three common paths to an implicit or explicit None:

The function has no return statement at all.

The function executes a bare return with no expression after it.

Execution falls through a branch where no return is reached. def sign(n):

if n > 0:

return 'positive'

if n == 0:

return

  • print(sign(5))
  • print(sign(0))
  • print(sign(-2))

The three calls print positive , None , and None . The zero call hits a bare return ; the negative call runs off the end of the body. Both yield None by different routes, and PCEP loves to mix the two in one snippet.

Reading nested calls

When calls are nested, resolve the innermost call first, then pass its return value outward. def add_one(n):

return n + 1

def show(n):

  • print(n)
  • return n * 10

answer = show(add_one(2))

First add_one(2) returns 3 . Then show(3) prints 3 and returns 30 . Finally answer becomes 30 . Note a name without parentheses is just a reference: tool = show runs no body, while tool(3) does.

A repeatable exam tracing routine

Use this checklist on every function snippet so you never confuse output, function objects, and return values:

Mark every def as a definition only; do not execute its body yet.

Find the first actual call at the top level.

Bind arguments to parameters for that call.

Trace the body until a return , an exception, or the natural end.

Replace the entire call expression with the value it returned.

Continue at the line after the call.

Worked trap. Consider x = print('hi') . Many candidates pick 'hi' as the value of x . Wrong: print displays hi but evaluates to None , so x is None . Then print(x + 1) would raise TypeError because None + 1 is invalid. Recognizing that print returns None is worth several marks across the exam.

The routine is slower than guessing but far faster than rereading a snippet three times. On PCEP, the difference between definition and call, and between print and return, is frequently the entire question.

More patterns the exam reuses

A few additional behaviors of call flow show up repeatedly and are worth drilling until automatic.

A function object is a first-class value. You can store it, pass it, and call it later. The presence or absence of parentheses is the whole difference between referencing and calling. In funcs = [double] , no body runs; funcs[0](5) is what finally calls it. PCEP often plants a bare function name in a list or assignment to see whether you mistake the reference for a call result.

Multiple return values are really one tuple. Writing return a, b returns a single tuple (a, b) . The caller can unpack it with x, y = f() . If a snippet stores the result in one variable, that variable holds the whole tuple, not just the first item.

A return inside a loop exits the entire function , not just the loop. Compare with break , which only leaves the loop and lets later statements run: def first_even(nums):

for n in nums:

if n % 2 == 0:

  • return n
  • return None

print(first_even([1, 3, 4, 7]))

This returns 4 the instant the loop finds an even number; the loop and the function both end, and the trailing return None never runs. Whenever you see a return inside a loop, stop tracing the loop the moment it fires.

Quick contrast table:

Statement Leaves loop? Leaves function?

  • break Yes No
  • continue No (skips to next iteration) No
  • return Yes Yes

Keeping these three apart prevents the classic mistake of thinking code after a return -in-a-loop still executes.