Python Functions and Argument Passing

Introduction to Functions and Code Reuse

  • Concept of Functions:

    • A function is a named block of code designed to perform a specific task when called (invoked).

    • Functions enable code reusability, improve organizational structure, and enhance code readability.

    • Functions can take zero or more parameters as input and optionally produce return values.

  • Primary Motivations for Writing Functions:

    • Code Duplication Isolation (Condition 1): When a specific piece of code is repeated multiple times throughout a program, it should be isolated into a standalone function.

    • Single-Point Maintenance: By encapsulating repeated code into a function, any future modifications or bug fixes need to be applied only once within the function body, automatically propagating the change across all invocation points.

Software Decomposition and Design Principles

  • Code Decomposition (Condition 2):

    • When a codebase or problem becomes too large and complex to comprehend easily, it must be decomposed into smaller, isolated, and self-contained sub-problems.

    • Each sub-problem is implemented as a separate function.

    • This iterative process continues until the codebase consists of short, single-purpose functions that are easy to understand, debug, and unit-test independently.

Decomposition of a complex problem into smaller parts
  • Teamwork and Responsibility Division (Condition 3):

    • Large software engineering projects cannot be executed efficiently by a single developer; work must be distributed across a team.

    • Decomposition allows different programmers to simultaneously write separate, well-defined functions without interfering with each other's code.

    • Individual functions are packaged together into modules to construct the final cohesive product.

Division of programming work among team members

Origins and Categories of Functions in Python

  • Four Basic Categories of Functions in Python:

    • Built-in Functions:

    • Functions integrated directly into the Python core language.

    • Always available for execution without requiring import statements or extra setup (e.g., print(), input()).

    • The full official directory is available at https://docs.python.org/3/library/functions.html.

    • Pre-installed Module Functions:

    • Functions packaged within modules that ship standard with Python installations.

    • Used less frequently than built-in functions; require explicit import statements (e.g., import math) to make them accessible within code.

    • User-Defined Functions:

    • Custom functions written directly by programmers to satisfy specific application requirements.

    • Lambda Functions:

    • Small, anonymous, single-expression functions defined inline.

Sources of functions in Python

Defining User-Defined Functions and Execution Mechanics

  • Syntax Rules for Function Definition:

    • A function definition must begin with the keyword def (short for define).

    • The keyword is followed by the functionName, which must comply with standard Python variable naming conventions.

    • The name is immediately followed by a pair of parentheses (), which enclose any optional parameters.

    • The header line must conclude with a colon :

    • The function body begins on the subsequent line and must be indented (nested) by at least one level. It contains one or more instructions executed whenever the function is invoked.

    • The boundaries of a function body are defined strictly by indentation; the function ends where nesting/indentation returns to the previous outer level.

def functionName():
    functionBody
  • Control Flow During Function Invocation:

    • Invocation (Call): When Python encounters a function call, execution of the main program halts temporarily, saving the current position.

    • Jump & Execution: Program control jumps to the function body, executing its instructions sequentially from top to bottom.

    • Return: Reaching the end of the function body (or encountering an explicit return) forces control to jump back to the exact invocation site, resuming execution at the statement directly following the function call.

Function invocation and return execution flow
  • Essential Operational Constraints (Catches):

    • Definition Order Requirement: A function cannot be invoked before it has been defined. Because Python interprets script files top-to-bottom line-by-line, it will not look ahead to discover functions declared further down the file.

    • Name Collision / Overwriting: A function and a variable cannot share the same identifier in the same scope. Defining a variable with the same name as an existing function (or vice versa) overwrites the original definition.

Function Parameters, Arguments, and Variable Shadowing

  • Parameters vs. Arguments:

    • Parameter: A specialized variable defined within the parentheses of a function header statement (def). Parameters live exclusively inside their defining function.

    • Argument: A value or expression passed into a function during invocation to initialize a parameter. Arguments exist outside the function body.

Function taking inputs a and b to produce output c
  • Single-Parameter Functions:

    • Modifies function behavior based on input values passed by the caller.

def message(number):
    print("Enter a number:", number)

message(1)
# Output: Enter a number: 1
  • Variable Shadowing:

    • Occurs when a parameter shares the exact same name as a global or outer-scope variable.

    • Within the function, the local parameter shadows (hides) the outer variable.

    • The parameter and the outer variable remain two completely distinct memory entities; modifying or accessing the parameter inside the function has no effect on the outer variable.

def message(number):
    print("Enter a number:", number)

number = 1234
message(1)
# Output: Enter a number: 1

print(number)
# Output: 1234
  • Multi-Parameter Functions:

    • Functions can declare multiple parameters separated by commas.

    • Callers must provide an equal number of arguments matching the parameter signature.

def message(what, number):
    print("Enter", what, "number", number)

message("telephone", 11)
# Output: Enter telephone number 11

message("price", 5)
# Output: Enter price number 5

message("number", "number")
# Output: Enter number number number

Parameter Passing Techniques: Positional, Keyword, and Mixed

  • Positional Argument Passing:

    • Maps the ithi^{\text{th}} argument supplied during invocation directly to the ithi^{\text{th}} parameter declared in the function header based on sequential position.

def myFunction(a, b, c):
    print(a, b, c)

myFunction(1, 2, 3)
# Output: 1 2 3
  • Keyword Argument Passing (Named Arguments):

    • Passes values explicitly assigned to parameter names using the syntax param_name=value.

    • The order of arguments during invocation does not matter, as target destinations are determined strictly by name matching.

def introduction(firstName, lastName):
    print("Hello, my name is", firstName, lastName)

introduction(firstName="James", lastName="Bond")
# Output: Hello, my name is James Bond

introduction(lastName="Skywalker", firstName="Luke")
# Output: Hello, my name is Luke Skywalker
  • Mixing Positional and Keyword Arguments:

    • Positional and keyword argument styles can be combined within a single function invocation.

    • Unbreakable Rule: All positional arguments MUST appear before any keyword arguments in a function call.

def adding(a, b, c):
    print(a, "+", b, "+", c, "=", a + b + c)

# Purely positional
adding(1, 2, 3)
# Output: 1 + 2 + 3 = 6

# Purely keyword
adding(c=1, a=2, b=3)
# Output: 2 + 3 + 1 = 6

# Mixed (valid: positional first, then keyword)
adding(3, c=1, b=2)
# Output: 3 + 2 + 1 = 6
  • Syntax and Type Errors in Argument Passing:

    • SyntaxError (Positional After Keyword): Placing a positional argument after a keyword argument causes a parser failure before execution.

def subtra(a, b):
    print(a - b)

subtra(a=5, 2)
# Output: SyntaxError: positional argument follows keyword argument
  • TypeError (Multiple Value Assignment): Supplying a positional argument that maps to a parameter already assigned via a keyword argument causes a runtime error.

def adding(a, b, c):
    print(a + b + c)

adding(3, a=1, b=2)
# Output: TypeError: adding() got multiple values for argument 'a'

Default Parameter Values and Fallback Handling

  • Defining Default Parameter Values:

    • Functions can specify pre-defined fallback values for parameters in the def header line using the syntax parameter=default_value.

    • If the invoker omits a parameter during execution, Python automatically uses its pre-defined default value.

def introduction(firstName, lastName="Smith"):
    print("Hello, my name is", firstName, lastName)

introduction("James", "Doe")
# Output: Hello, my name is James Doe

introduction("Henry")
# Output: Hello, my name is Henry Smith

introduction(firstName="William")
# Output: Hello, my name is William Smith
  • Multiple Default Parameters:

    • Every parameter in a function header can be assigned a default value.

    • Omitted arguments fall back to defaults, while provided arguments override default assignments.

def introduction(firstName="John", lastName="Smith"):
    print("Hello, my name is", firstName, lastName)

introduction()
# Output: Hello, my name is John Smith

introduction(lastName="Hopkins")
# Output: Hello, my name is John Hopkins
  • Summary of Function Parameter Examples:

# 1-parameter function
def hi(name):
    print("Hi,", name)

hi("Greg")

# 2-parameter function
def hiAll(name1, name2):
    print("Hi,", name1)
    print("Hi,", name2)

hiAll("Sebastian", "Konrad")

# 3-parameter function
def address(street, city, postalCode):
    print("Your address is:", street, "St.,", city, postalCode)

s = input("Street: ")
pC = input("Postal Code: ")
c = input("City: ")
address(s, c, pC)