Python Function Examples – How to Declare and Invoke with Parameters

Functions are one of the most powerful and essential concepts in programming. In Python, functions allow you to organize your code into reusable, self-contained blocks that perform a specific task. They help make your code more modular, readable, and maintainable.

In this article, we‘ll dive deep into Python functions – how to define them, how to call them, and how to use parameters to make them more flexible and useful. We‘ll look at lots of examples along the way. By the end, you‘ll have a thorough understanding of Python functions and be able to leverage them effectively in your own programs.

Anatomy of a Python Function

Before we get to invoking functions, let‘s break down the anatomy of a function definition in Python. Here‘s the general syntax:

def function_name(parameter1, parameter2, ...):
    """docstring"""
    # function body
    statement1
    statement2
    ...
    return [expression] 

Let‘s go through each part:

  • def – The def keyword tells Python you‘re defining a new function.
  • function_name – This is the name of your function. It should be lowercase, with words separated by underscores. Choose a name that clearly indicates what the function does.
  • parameters – These are optional placeholder names for values the function expects to receive when called. They go inside the parentheses, separated by commas if there are multiple.
  • docstring – This is an optional, but recommended, string that describes what the function does. It‘s surrounded by triple quotes.
  • function body – This is the code that runs whenever the function is called. It must be indented under the def line.
  • return – Functions can optionally return a value back to the caller using the return keyword, followed by the value or expression to return. If there‘s no return statement, the function implicitly returns None.

Here‘s a simple example that puts it all together:

def greet(name):
    """Display a friendly greeting."""
    print(f"Hello, {name}! Welcome.")

greet("Alice")  
# Output: Hello, Alice! Welcome.

In this example, we define a function named greet that expects one parameter, name. Whenever greet is called, it prints a friendly greeting message that includes the provided name.

We invoke or call the function on the last line by writing its name followed by parentheses. Inside the parentheses, we pass in the value we want the function to use for the name parameter.

Positional vs Keyword Arguments

In the greet example above, we called the function using a positional argument – "Alice" was bound to the first (and only) parameter, name.

Python also supports calling functions with keyword arguments, where you explicitly specify which parameter each argument corresponds to. Here‘s an example:

def describe_pet(animal, name):
    """Display information about a pet."""
    print(f"I have a {animal} named {name}.")

describe_pet(animal="hamster", name="Harry") 
describe_pet(name="Harry", animal="hamster")

As you can see, keyword arguments free you from having to remember the order of the parameters. This can make your code more readable, especially for functions that take many parameters.

You can mix positional and keyword arguments when calling a function. However, all positional arguments must come before any keyword arguments:

describe_pet("dog", name="Spot")  # This works
describe_pet(animal="cat", "Whiskers")  # This does NOT work

Here‘s another example showing a mix of positional and keyword arguments:

def mysum(a, b, c=0, d=0):
    """Return the sum of four numbers, with the last two being optional."""
    return a + b + c + d

assert mysum(1, 2) == 3
assert mysum(1, 2, 3) == 6
assert mysum(1, 2, c=3) == 6
assert mysum(1, 2, d=4) == 7
assert mysum(1, 2, 3, 4) == 10

Default Parameter Values

You may have noticed in the previous example that some parameters (c and d) had values assigned to them in the function definition:

def mysum(a, b, c=0, d=0):
    ...

These are called default parameter values. They allow the caller to omit arguments for those parameters if they want. Default values make your function more versatile. Common use cases are for optional configuration values or initial accumulators.

When calling a function, arguments for parameters with defaults can be omitted entirely:

mysum(3, 4)  # c and d use their default values of 0

Or specified using either positional or keyword arguments:

mysum(1, 2, 3)  # positional: a=1 b=2 c=3 d=0
mysum(3, 4, d=5)  # keyword: a=3 b=4 c=0 d=5 

A few best practices to keep in mind with default arguments:

  • Always put parameters without defaults before those with defaults in your function definition.
  • Don‘t use mutable objects like lists or dictionaries as default values, as it can lead to surprising behavior. Use None instead.
  • Avoid changing the value of default parameters inside your function body, as it can make the code harder to understand.

Returning Values

As we‘ve seen, functions can accept input in the form of parameters. But they can also send output back to the caller using the return keyword.

When a return statement is executed, the function exits immediately and the value of the expression following return is sent back to the caller.

def square(n):
    """Return the square of a number."""
    return n ** 2

assert square(3) == 9
assert square(-2) == 4

x = 5
y = square(x) 
assert y == 25

A single function can have multiple return statements, but only one will ever be executed:

def absolute_value(x):
    """Return the absolute value of a number."""
    if x < 0:
        return -x
    return x

assert absolute_value(-3) == 3
assert absolute_value(7) == 7  

If a function doesn‘t explicitly return a value, it implicitly returns None:

def do_nothing():
    pass

result = do_nothing()
assert result is None

Variable Scope

When you define a variable inside a function, that variable is local to the function and cannot be accessed from outside:

def myfunc():
    x = 100
    print(f"Inside: {x}")

myfunc()  # prints: Inside: 100
print(x)  # NameError: name ‘x‘ is not defined

This is an example of variable scoping. Variables defined inside a function are in the local scope of that function, while variables defined outside any function are in the global scope.

You can read global variables from inside a function:

x = 100

def myfunc():
    print(f"Inside: {x}")

myfunc()  # prints: Inside: 100

But if you try to modify a global variable inside a function, you‘ll get an error:

x = 100

def myfunc():
    x += 1  # UnboundLocalError: local variable ‘x‘ referenced before assignment

myfunc()

To modify a global variable inside a function, you must declare it as global:

x = 100

def myfunc():
    global x
    x += 1

myfunc()
assert x == 101

However, using global variables is generally discouraged, as it can make your code harder to understand and debug. It‘s better to pass any needed values into functions as parameters and return any results.

Recursive Functions

A recursive function is a function that calls itself. They provide an elegant way to solve problems that can be broken down into smaller subproblems.

Here‘s a classic example that uses recursion to calculate the factorial of a number:

def factorial(n):
    """Return n! (n factorial)."""
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

assert factorial(0) == 1  
assert factorial(1) == 1
assert factorial(5) == 120

The factorial function calls itself with n-1 until the base case of n == 0 is reached.

Another example of recursion is the Fibonacci sequence:

def fib(n):
    """Return the nth Fibonacci number."""
    if n < 0:
        raise ValueError("n must be >= 0")
    if n == 0 or n == 1:
        return n
    else:
        return fib(n-1) + fib(n-2)

assert fib(0) == 0
assert fib(1) == 1
assert fib(2) == 1
assert fib(3) == 2
assert fib(10) == 55

While recursion can be elegant, it‘s not always the most efficient approach. Deep recursion can lead to stack overflow errors. And recursive solutions often have a more concise iterative equivalent:

def fib_iter(n):
    """Return the nth Fibonacci number, computed iteratively."""
    if n < 0:
        raise ValueError("n must be >= 0") 
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

assert all(fib_iter(i) == fib(i) for i in range(20))  # Equivalent to recursive version

Lambda Functions

Python supports the creation of anonymous functions using the lambda keyword. Anonymous functions are called lambdas.

A lambda function can take any number of arguments, but can only have one expression:

double = lambda x: x * 2

assert double(3) == 6
assert double(-4) == -8

sum_two = lambda x, y: x + y
assert sum_two(1, 2) == 3

product = lambda x, y, z: x * y * z
assert product(2, 3, 4) == 24

Lambda functions are commonly used as arguments to higher-order functions like map, filter, and sort:

numbers = [1, 2, 3, 4, 5]

squared = list(map(lambda x: x**2, numbers))
assert squared == [1, 4, 9, 16, 25] 

even = list(filter(lambda x: x % 2 == 0, numbers))
assert even == [2, 4]

words = [‘apple‘, ‘banana‘, ‘cherry‘, ‘date‘]
words.sort(key=lambda w: len(w))  
assert words == [‘date‘, ‘apple‘, ‘banana‘, ‘cherry‘]

While lambdas can be useful for short, throwaway functions, named functions using def are generally more readable and maintainable for non-trivial cases.

Best Practices

Here are some general best practices to keep in mind when working with Python functions:

  • Give your functions and parameters descriptive names that indicate their purpose.
  • Keep your functions focused on a single task. If a function is doing too many things, consider breaking it into smaller functions.
  • Use default parameter values for optional arguments.
  • Don‘t change mutable default parameter values inside your function body.
  • Use return to send values back to the caller.
  • Keep your functions small and readable. Generally, 10-15 lines of code is a good maximum size.
  • Add docstrings to describe what your function does, what parameters it expects, and what it returns.
  • Consider using type hints to document the expected types of parameters and return values.
  • Avoid modifying global variables inside functions. Pass values as parameters instead.
  • Don‘t be afraid to make helper functions that are called by your main functions. This can help keep your code organized and readable.

Conclusion

Functions are an essential part of Python programming. They allow you to write reusable, organized, and maintainable code. In this article, we‘ve covered all the key aspects of Python functions, including:

  • How to define functions with the def keyword
  • How to call functions with positional, keyword, and default arguments
  • How to return values from functions
  • Variable scoping rules
  • Recursive functions
  • Lambda functions
  • Best practices for writing clear, maintainable functions

With this knowledge, you‘re well-equipped to start leveraging the power of functions in your Python programs. Remember, the key to mastering functions is practice. So get out there and start writing some functions of your own!

Similar Posts