Python 3 - Functions
Python 3 - Functions
Section titled “Python 3 - Functions”Functions are named blocks of reusable code designed to perform a specific task. They are essential for structuring applications, improving code organization, and promoting reusability (following the DRY principle - Don’t Repeat Yourself).
Python provides many built-in functions (like print(), len(), type()), but you can also define your own, known as user-defined functions.
Defining a Function
Section titled “Defining a Function”Key rules for defining a function in Python:
- Start with the keyword
deffollowed by the function name and parentheses(). Function names follow the same rules as variable names (letters, numbers, underscore; cannot start with a number). Conventionally, function names usesnake_case. - Input parameters (arguments) the function accepts are placed inside the parentheses, separated by commas.
- The function header line ends with a colon
:. - The indented block of code following the colon is the function’s body (suite).
- Optional: The first statement in the body can be a string literal called a docstring (documentation string), enclosed in triple quotes
"""Docstring goes here""". It describes the function’s purpose. - Use the
returnstatement to exit the function and optionally send back a value (or values) to the caller. Areturnwithout an expression returnsNone. If there’s noreturnstatement, the function implicitly returnsNoneupon reaching the end of its body.
Syntax
Section titled “Syntax”def function_name(parameter1, parameter2, ...): """Optional docstring explaining the function.""" # Function body (indented code) statement1 statement2 ... return [value] # Optional return statementType Hinting (Optional but Recommended): Modern Python encourages adding type hints for parameters and the return value for better code clarity and tooling support (like static analysis). They don’t affect runtime behavior by default.
def greet(name: str) -> None: """Greets the user by name.
Args: name: The name of the person to greet. """ print(f"Hello, {name}!")
def add(a: int, b: int) -> int: """Returns the sum of two integers.""" return a + bExample: Simple Function
Section titled “Example: Simple Function”# Defining the functiondef print_message(message: str): """Prints the provided message to the console.""" print(message) # Implicitly returns NoneCalling a Function
Section titled “Calling a Function”Defining a function just creates it. To execute the code inside, you must call the function by using its name followed by parentheses, providing any required arguments.
#!/usr/bin/env python3
# Function definitiondef print_message(message: str): """Prints the provided message to the console.""" print(message)
# --- Calling the function ---print_message("This is the first call!")print_message("This is the second call.")
result = print_message("Calling and assigning result...") # Function executesprint(f"The function returned: {result}") # Output: NoneOutput:
This is the first call!This is the second call.Calling and assigning result...The function returned: NoneArgument Passing: Pass by Object Reference
Section titled “Argument Passing: Pass by Object Reference”Python’s argument passing mechanism is often described as “pass by object reference” or “pass by assignment”. What this means:
- When you pass an object (like a number, string, list) to a function, the function parameter becomes a new reference (name) pointing to that same object.
- If the object passed is mutable (e.g., a list, dictionary), modifications made to the object’s contents inside the function will affect the original object outside the function.
- If you reassign the parameter variable inside the function to a new object, this only changes the local reference within the function; it does not affect the original variable outside the function.
Example 1: Modifying a mutable object (list)
#!/usr/bin/env python3
def modify_list(some_list: list): """Appends an item to the passed list.""" print(f"Inside function (before change): {some_list}, ID: {id(some_list)}") some_list.append(100) # Modifies the original list object print(f"Inside function (after change): {some_list}, ID: {id(some_list)}")
my_list = [10, 20, 30]print(f"Outside function (before call): {my_list}, ID: {id(my_list)}")modify_list(my_list)print(f"Outside function (after call): {my_list}, ID: {id(my_list)}")Output (IDs will vary but show the same list object being used):
Outside function (before call): [10, 20, 30], ID: <some_id>Inside function (before change): [10, 20, 30], ID: <some_id>Inside function (after change): [10, 20, 30, 100], ID: <some_id>Outside function (after call): [10, 20, 30, 100], ID: <some_id>Example 2: Reassigning the parameter
#!/usr/bin/env python3
def reassign_list(some_list: list): """Reassigns the parameter to a new list.""" print(f"Inside function (before reassign): {some_list}, ID: {id(some_list)}") some_list = [1, 2, 3, 4] # Creates a NEW local list print(f"Inside function (after reassign): {some_list}, ID: {id(some_list)}")
my_list = [10, 20, 30]print(f"Outside function (before call): {my_list}, ID: {id(my_list)}")reassign_list(my_list)print(f"Outside function (after call): {my_list}, ID: {id(my_list)}") # Original is unchangedOutput (IDs will vary but show the parameter getting a new ID after reassignment):
Outside function (before call): [10, 20, 30], ID: <original_id>Inside function (before reassign): [10, 20, 30], ID: <original_id>Inside function (after reassign): [1, 2, 3, 4], ID: <new_id>Outside function (after call): [10, 20, 30], ID: <original_id>Function Arguments Types
Section titled “Function Arguments Types”Python offers flexibility in how arguments can be passed to functions:
- Positional Arguments: Passed in the order they are defined in the function signature. The number of arguments must match unless defaults are specified.
- Keyword Arguments: Passed using
parameter_name=valuesyntax. The order doesn’t matter, and they can be mixed with positional arguments (positional must come first). - Default Arguments: Parameters are given a default value in the function definition (
def func(param=default):). If the caller doesn’t provide a value for that parameter, the default is used. - Variable-Length Positional Arguments (
*args): Collects any extra positional arguments into a tuple. - Variable-Length Keyword Arguments (
**kwargs): Collects any extra keyword arguments into a dictionary.
More advanced (Python 3+): Positional-only (/) and keyword-only (*) arguments enforce how arguments can be passed.
Required/Positional Arguments
Section titled “Required/Positional Arguments”These must be provided in the correct order.
#!/usr/bin/env python3def describe_pet(animal_type: str, pet_name: str): print(f"I have a {animal_type} named {pet_name}.")
describe_pet("hamster", "Harry") # Correct call# describe_pet("Whiskers") # TypeError: missing 1 required positional argument: 'pet_name'Keyword Arguments
Section titled “Keyword Arguments”Order doesn’t matter when using keywords.
#!/usr/bin/env python3def describe_pet(animal_type: str, pet_name: str): print(f"I have a {animal_type} named {pet_name}.")
describe_pet(pet_name="Willie", animal_type="dog") # Order changed, but worksdescribe_pet("cat", pet_name="Whiskers") # Mixing positional and keywordDefault Arguments
Section titled “Default Arguments”Provide flexibility by making some arguments optional. Default values are evaluated once when the function is defined.
Important: Avoid using mutable objects (like lists or dictionaries) as default values, as they can lead to unexpected behavior because the same object is reused across calls. Use None as the default and create the mutable object inside the function if needed.
#!/usr/bin/env python3def power(base: float, exponent: float = 2.0): """Calculates base raised to the exponent power.""" return base ** exponent
print(f"5 squared: {power(5)}") # Uses default exponent=2print(f"2 cubed: {power(2, 3)}") # Overrides defaultprint(f"3 squared: {power(base=3)}") # Keyword arg, uses default exponentVariable-Length Arguments (*args and **kwargs)
Section titled “Variable-Length Arguments (*args and **kwargs)”Used when you don’t know in advance how many arguments might be passed.
#!/usr/bin/env python3def make_pizza(size: str, *toppings: str, **customer_info: str): """Summarizes the pizza being ordered.""" print(f"\nMaking a {size}-inch pizza with the following toppings:") if toppings: for topping in toppings: print(f"- {topping}") else: print("- Plain cheese")
if customer_info: print("\nCustomer Info:") for key, value in customer_info.items(): print(f"- {key}: {value}")
# Calling the functionmake_pizza("12", "mushrooms", "green peppers", "extra cheese")make_pizza("16", delivery="yes", name="John Doe")make_pizza("9", "pepperoni", name="Jane", phone="555-1234", vip=True)*toppings collects all extra positional arguments (‘mushrooms’, ‘green peppers’, …) into a tuple named toppings.
**customer_info collects all extra keyword arguments (delivery='yes', name='John Doe', …) into a dictionary named customer_info.
Anonymous Functions (lambda)
Section titled “Anonymous Functions (lambda)”Lambda functions provide a way to create small, anonymous functions inline, typically for short operations where a full def statement is overly verbose.
- Syntax:
lambda arguments: expression - Can take any number of arguments but can only have one expression.
- The expression’s value is implicitly returned.
- Cannot contain complex statements (like assignments, loops, try/except).
- Often used with functions like
map(),filter(),sorted(), or in GUI callbacks.
#!/usr/bin/env python3
# Lambda function to add two numbersadd = lambda x, y: x + yprint(f"Lambda add(5, 3): {add(5, 3)}")
# Using lambda with sorted()points = [(1, 2), (3, 1), (5, -4), (2, 3)]# Sort points based on the second element (y-coordinate)points_sorted_by_y = sorted(points, key=lambda point: point[1])print(f"Points sorted by y: {points_sorted_by_y}")The return Statement
Section titled “The return Statement”The return statement exits the current function execution and sends a value (or None) back to the point where the function was called.
A function can return any type of Python object, including tuples, lists, dictionaries, or even other functions. To return multiple values, simply list them separated by commas; Python automatically packs them into a tuple.
#!/usr/bin/env python3
def calculate_stats(numbers: list) -> tuple: """Calculates the sum and average of a list of numbers.""" if not numbers: return (0, 0.0) # Return tuple for empty list total = sum(numbers) average = total / len(numbers) return total, average # Returns a tuple (total, average)
nums = [10, 20, 30, 40]stats_result = calculate_stats(nums)
print(f"Stats for {nums}: Sum={stats_result[0]}, Average={stats_result[1]}")
# Or unpack the returned tuple directlysum_val, avg_val = calculate_stats(nums)print(f"Unpacked: Sum={sum_val}, Average={avg_val}")Variable Scope (LEGB Rule)
Section titled “Variable Scope (LEGB Rule)”Scope determines the visibility of a variable within different parts of your program. Python uses the LEGB rule to resolve names:
- Local: Names assigned within a function (and not declared
globalornonlocal). - Enclosing function locals: Names in the local scope of any enclosing functions (for nested functions).
- Global: Names assigned at the top level of a module file, or declared
globalin a function. - Built-in: Names preassigned in Python (like
print,len,Exception).
Local variables exist only within the function where they are defined. Global variables are accessible throughout the module.
#!/usr/bin/env python3
global_var = 100 # Global scope
def my_func(): local_var = 50 # Local scope print(f"Inside function: local_var = {local_var}") print(f"Inside function: global_var = {global_var}") # Can access global
my_func()print(f"Outside function: global_var = {global_var}")# print(f"Outside function: local_var = {local_var}") # NameError: name 'local_var' is not definedUse the global keyword inside a function if you need to modify a global variable, and nonlocal to modify a variable in an enclosing function’s scope (not the global scope).