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 thedef
line.return
– Functions can optionally return a value back to the caller using thereturn
keyword, followed by the value or expression to return. If there‘s no return statement, the function implicitly returnsNone
.
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!