Python Function

A Python function is a reusable block of code designed to perform a specific task. Functions allow you to break down your code into smaller, modular parts, making it easier to manage and reuse.

Defining a Function

In Python, you can define a function using the def keyword, followed by the function name and a pair of parentheses containing any parameters.

For example:

def greet():
    print("Hello, James!")

Basic Syntax of a Python Function

def function_name(parameter1, parameter2, …):
    """Docstring: Brief description of the function.""" 
    # Function body 
    return value

Breakdown of Components:

  1. def keyword: Defines the function. It stands for “define”.
  2. function_name: The name of the function.
  3. parameters (optional): Values the function accepts as input.
  4. Colon :: Indicates the beginning of the function body.
  5. """Docstring""" (optional): A brief description of the function’s purpose, used for documentation.
  6. Function Body: Contains the code to be executed when the function is called.
  7. return (optional): Returns a value to the caller. If omitted, the function returns None.

Calling a Function

You can call a function by using its name followed by parentheses. If the function accepts parameters, you pass them inside the parentheses.

For example:

def greet():
    print("Hello, James!")


# Calling the function
greet()  # Output: Hello, James!

Function Arguments

In Python, functions can take arguments (inputs) to perform tasks.

Arguments or Parameters

Parameters and arguments are often used interchangeably in Python functions, but they have distinct meanings.

Parameters are the variables defined within the parentheses of a function declaration. They act as placeholders for values that will be passed to the function when it is called.

For example:

def introduce(name, age):
    print(f"My name is {name} and I am {age} years old.")

In this example, name and age are parameters.

Arguments are the actual values passed to a function when it is called. They replace the placeholders (parameters) with specific data.

For example:

introduce(name="James", age=27)

Here, "James" and 27 are arguments.

Types of Arguments

(1) Positional Arguments: 

These are the most common types. The order of the arguments matters; they must be provided in the same order as the parameters in the function definition.

For example:

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


subtract(5, 3)  # Output: 2

(2) Keyword Arguments

You can specify arguments by their parameter names, which allows you to pass them in any order.

For example:

def introduce(name, age):
    print(f"My name is {name} and I am {age} years old.")


introduce(age=27, name="James")   # Order doesn’t matter

Output: # My name is James and I am 27 years old.

(3) Default Arguments

You can provide default values for parameters. If no argument is passed, the default value is used. 

For example:

def greet(name="Guest"):
    print(f"Hello, {name}!")


greet()  # Uses the default value.
# Output: Hello, Guest!

greet("James")  # Overrides the default value
# Output: Hello, James!

(4) Variable-Length Arguments

In Python, you can create functions that accept an arbitrary number of arguments. This is useful when you don’t know in advance how many arguments will be passed. Python handles this with *args for positional arguments and **kwargs for keyword arguments. 

(a) *args

It allows a function to accept any number of positional arguments. The arguments passed to *args are collected into a tuple inside the function.

For example:

def sum_all(*numbers):
    return sum(numbers)


print(sum_all(2, 3))  # Output: 5
print(sum_all(2, 3, 5))  # Output: 10

(b) **kwargs

It allows a function to accept any number of keyword arguments (i.e., arguments passed as key=value). The keyword arguments are collected into a dictionary inside the function.

For example:

def print_info(**details):
    for key, value in details.items():
        print(f"{key}: {value}")


print_info(name="James", age=27)

Output:

name: James
age: 27

The return Statement

The return statement in Python is used to send a value back to the caller of a function. It terminates the execution of the function and transfers control back to the point where the function was called.

Basic Syntax:

return value

Example:

def add(x, y):
    result = x + y
    return result


sum = add(2, 3)
print(sum)  # Output: 5

A function can return values of any type such as numbers, strings, lists, tuples, dictionaries, or even other functions. If a function doesn’t have a return statement, it returns None by default.

Returning Multiple Values

In Python, a function can return multiple values as a tuple. To do this, simply separate the values with commas in the return statement.

For example:

def calculate(a, b):
    return a + b, a - b, a * b


result = calculate(5, 3)
print(result)  # Output: (8, 2, 15)

# Unpacking the tuple
sum_value, diff_value, prod_value = result

print(sum_value)  # Output: 8
print(diff_value)  # Output: 2
print(prod_value)  # Output: 15

The pass Statement

The pass statement in Python is a placeholder that does nothing when executed. It is often used when you need a statement but don’t have anything specific to do yet.

If you want to define an empty class or function as a placeholder, you can use pass.

For example:

def empty_function():
    pass # Placeholder function does nothing for now

class My_Class:
    pass # Placeholder class

Variable Scope in Function

In Python, the scope of a variable refers to the region of a program where a variable can be accessed or modified.

Types of Variable Scopes

There are four main types of variable scope in Python:

  • Local Scope
  • Global Scope
  • Enclosing Scope (Nonlocal Scope)
  • Built-in Scope

(1) Local Scope

Variables defined inside a function have local scope, meaning they can only be accessed within that function.

For example:

def my_function():
    x = 10  # Local variable
    print(x)


my_function()  # Output: 10
print(x)  # NameError: name 'x' is not defined

If a local variable has the same name as a global variable, the local variable takes precedence within the function.

For example:

x = 5  # Global variable


def my_function():
    x = 10
    print(x)


my_function()  # Output: 10
print(x)  # Output: 5

(2) Global Scope

Variables defined outside of function have global scope, meaning they can be accessed from anywhere in the program.

For example:

x = 5  # Global variable


def my_function():
    print(x)


my_function()  # Output: 5
print(x)  # Output: 5

However, to modify a variable inside a function, you must use the global keyword.

For example:

x = 5  # Global variable


def my_function():
    global x
    x = 10
    print(x)


my_function()  # Output: 10
print(x)  # Output: 10

(3) Enclosing Scope (Nonlocal Scope)

In Python, the enclosing scope refers to the scope of a function that contains another nested function. When a variable is defined in an outer function, it can be accessed from the inner function. However, modifying the outer function’s variable within the inner function requires the use of the nonlocal keyword.

For example:

def outer_function():
    x = 5  # Enclosing variable

    def inner_function():
        nonlocal x  # Modifying enclosing variable
        x = 10
        print(x)

    inner_function()  # Output: 10
    print(x)  # Output: 10 (enclosing variable changed)


outer_function()

(4) Built-in Scope

Python has a built-in namespace that contains constants like True, False, and None, functions such as print(), len(), and range(), and exceptions like ZeroDivisionError. These constants, functions, and exceptions are always available throughout the program.

For example:

# Built-in variables (constants)
print(True)  # Output: True
print(None)  # Output: None

# Built-in functions
print(len("Hello"))  # Output: 5

Anonymous (Lambda) Functions

Lambda functions are small, anonymous functions defined using the lambda keyword. They are often used for short, simple operations.

For example:

square = lambda x:x**2

print(square(3)) # Output: 9

Recursion

In programming, recursion is a technique where a function calls itself directly or indirectly. It is often used to solve problems that can be broken down into smaller, similar subproblems.

Key Concepts of Recursion

  1. Base Case: The condition under which the recursion stops. Without a base case, the function will call itself indefinitely, leading to a stack overflow.
  2. Recursive Case: The part where the function calls itself with a modified argument to approach the base case.

Here’s an example of a recursive factorial function:

def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1) # Recursive call

print(factorial(3)) # Output: 6

In this example:

  • Base case: When n == 1, the function returns 1 and stops.
  • Recursive case: The function keeps calling itself with n-1 until it reaches the base case.