Study guide

Parameters, Arguments, Defaults, and Keywords

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

Parameters, Arguments, Defaults, and Keywords

Key Takeaways Explain This Topic AI

Parameters are the names listed in a function definition; arguments are the actual values supplied during a call.

Positional arguments bind left to right; keyword arguments name a specific parameter regardless of order.

A required positional parameter cannot be omitted, and parameters with defaults must follow all required parameters.

Default expressions are evaluated once when the def statement runs, not on every call, which is the mutable-default trap.

Missing, duplicate, unexpected, or mis-ordered arguments all raise TypeError before the body runs.

Last updated: June 2026

Parameters versus arguments

A parameter is a name written in the function header. An argument is a value the caller supplies. PCEP deliberately uses meaningless names like a , b , and c so you cannot guess from semantics; you must bind the call mechanically. def power(base, exponent):

return base ** exponent

print(power(2, 3))

Here base and exponent are parameters; 2 and 3 are arguments. The call is positional, so base gets 2 , exponent gets 3 , and the result is 8 .

Positional and keyword binding

Arguments pass by position (matched left to right) or by keyword (the caller names the target parameter). Keyword arguments can reorder freely. def describe(name, score, passed=False):

return name + ':' + str(score) + ':' + str(passed)

  • print(describe('Mia', 82))
  • print(describe(score=91, name='Sol', passed=True))

Both calls are valid. The first uses the default passed=False ; the second rewrites the order but the keyword names keep the binding unambiguous, producing Sol:91:True .

Which call patterns are legal

  • Call pattern Valid? Reason
  • f(1, 2) Yes Positional values bind left to right
  • f(a=1, b=2) Yes Keywords name the parameters
  • f(a=1, 2) No A positional argument may not follow a keyword argument
  • f(1, a=2) Usually no If the first parameter is a , it receives two values
  • f(extra=1) No, unless extra is a parameter Unexpected keyword argument

Every one of these binding failures raises TypeError , and it raises before any line of the body executes. A PCEP item may ask whether the code runs at all, or which exception class best describes the failure.

The ordering rule is strict: positional arguments must precede keyword arguments in a call. Mixing them the other way is a SyntaxError (e.g. f(a=1, 2) ), which differs from the runtime TypeError you get from a duplicate or unexpected keyword. Knowing whether a snippet fails at parse time or call time is a recurring distinction.

Default values and the required-before-default rule

A parameter with a default becomes optional for the caller. Crucially, every parameter with a default must come after all required parameters in the header, or you get a SyntaxError . def total(price, tax=0.10, discount=0):

return price + price * tax - discount

  • print(total(100)) # uses both defaults -> 110.0
  • print(total(100, discount=5)) # default tax, override discount -> 105.0

Defaults do not make earlier required parameters optional. For def f(a, b=2) , calling f() still raises TypeError because a is unfilled; f(3) works and fills b from its default.

The mutable-default trap. Default expressions are evaluated once, when the def runs, not on each call. A mutable default such as [] is shared across calls: def collect(item, bag=[]):

  • bag.append(item)
  • return bag
  • print(collect(1)) # [1]
  • print(collect(2)) # [1, 2] <- same list reused

The safe entry-level habit is to default to immutable values (numbers, strings, booleans, or None ) and build the list inside the body.

Matching crowded calls by hand

When a call mixes positional and keyword arguments, write a binding table. def mix(a, b=10, c=20):

return a + b * c

print(mix(2, c=3))

  • Parameter Bound value Source
  • a 2 positional
  • b 10 default
  • c 3 keyword

The expression becomes 2 + 10 * 3 . Following operator precedence (multiplication first), that is 2 + 30 = 32 .

TypeError patterns to memorize

Watch for these recurring failures:

Too few required arguments ( power(2) for a two-parameter function).

Too many positional arguments.

The same parameter given twice, once positionally and once by keyword.

An unexpected keyword the function never declared.

A positional argument placed after a keyword argument.

When a snippet fails before its body starts, argument binding is almost always the cause. Trace the call header before tracing the body, and decide whether the error is SyntaxError (mis-ordered/keyword syntax) or TypeError (binding mismatch).

Arbitrary arguments: *args and **kwargs

PCEP-30-02 expects you to recognize, not necessarily design with, the two collecting parameters. A parameter written *args gathers any extra positional arguments into a tuple ; a parameter written **kwargs gathers any extra keyword arguments into a dictionary . def tally(first, *args, **kwargs):

return first, args, kwargs

print(tally(1, 2, 3, mode='fast'))

The output is (1, (2, 3), {'mode': 'fast'}) . first takes the single required positional 1 ; the leftover positionals 2 and 3 land in the tuple args ; the keyword mode='fast' lands in the dict kwargs . Knowing the container types (tuple for * , dict for ** ) is the most testable fact here.

  • Form Collects Resulting type
  • *args extra positional arguments tuple
  • **kwargs extra keyword arguments dict

Order in the header is fixed: ordinary parameters, then *args , then keyword-only parameters, then **kwargs . An empty call such as tally(9) simply yields (9, (), {}) : the tuple and dict are present but empty, never None . A common distractor claims args is a list; it is always a tuple. Another distractor claims missing extras raise an error; they do not, the containers are just empty.

Why this matters for tracing: when a header contains * or ** , count the required parameters first, then sweep the remaining positionals into the tuple and the remaining keywords into the dict. Get the partition right and the output follows directly.