🀲Functions

In this section, we will cover the basics of Python functions. Knowing how to use functions is essential for writing efficient and reusable code...

A function is a block of code that performs a specific task. It is like a subprogram that can be called from the main program. Functions take input arguments, perform operations, and return output values. Python provides several built-in functions, and we can also define our own functions to perform custom operations.

Benefits of Using Functions

There are several benefits of using functions in programming, including:

  1. Code Reusability: Functions allow us to reuse code by defining a block of code that can be called multiple times from different parts of the program.

  2. Modularity: Functions break down complex tasks into smaller, more manageable units, making the code more organized and easier to maintain.

  3. Abstraction: Functions provide an abstraction layer that hides the complexity of the code and makes it easier to use.

  4. Debugging: Functions make it easier to isolate and fix errors in code, making debugging a more straightforward process.

Types of function

There are two types of function in Python programming:

  • Standard library functions - These are built-in functions in Python that are available to use.

  • User-defined functions - We can create our own functions based on our requirements.

Defining and Calling Functions

Defining and calling functions is a fundamental concept in Python programming. Functions enable us to organize code into reusable blocks, which makes our programs easier to read, debug, and maintain. In this section, we will explore the process of defining and calling functions in Python.

Defining Functions

Defining a function in Python means creating a function object that maps the function name to the function.

a. Syntax

The syntax for defining a function in Python is:

def function_name(parameters):
    """docstring"""
    statement(s)

Where:

  • def keyword is used to define a function.

  • function_name is the name of the function. It should follow the same naming rules as for the identifiers.

  • parameters are the input arguments to the function. They are optional.

  • docstring is an optional documentation string that describes what the function does. It is enclosed within triple quotes.

  • statement(s) are the statements that are executed when the function is called. They should be indented with four spaces.

For example:

def greet(name):
    """This function greets the person passed in as parameter"""
    print("Hello, " + name + ". How are you doing?")

Here, greet is the name of the function, name is the input parameter, and the print() statement is executed when the function is called.

b. Parameters

Function parameters are the inputs that a function can accept. Parameters allow us to pass values or data to a function, which the function can then use to perform certain operations. In Python, function parameters are defined within the parentheses following the function name. There are two types of parameters:

  1. Positional parameters: These are mandatory parameters that are passed to a function in a specific order. The number of positional parameters should match the number of arguments passed to the function.

  2. Keyword parameters: These are optional parameters that can be passed to a function with a specific keyword. They have default values assigned to them, which are used when no value is passed for them during function call.

For example:

def greet(name, message="Hello"):
    print(message + ", " + name)

In this example, name is a positional parameter and message is a keyword parameter with a default value of "Hello". We can call this function using either positional or keyword arguments:

greet("John")   # prints "Hello, John"
greet("Jane", "Hi")   # prints "Hi, Jane"
greet(message="Hi", name="Alice")   # prints "Hi, Alice"

Note that when using keyword parameters, the order in which they are passed does not matter, as long as their keywords match the parameter names in the function definition.

c. Return Values

In Python, functions can return values using the return statement. The return statement can be followed by a value or an expression that is evaluated to return the value. The general syntax for returning values from a function is as follows:

def function_name(parameters):
    # function body
    return value

Here, the return statement is used to return the value to the calling function. The value can be of any data type, such as an integer, float, string, or even a list, dictionary, or tuple.

When a function returns a value, it can be assigned to a variable, used in an expression, or printed to the console using the print() function.

For example, the following function calculates the sum of two numbers and returns the result:

def sum_numbers(a, b):
    result = a + b
    return result

The function can be called and the returned value can be printed to the console using the following code:

result = sum_numbers(10, 20)
print(result)

This will output 30 to the console, which is the result of adding 10 and 20.

Examples

Here's an example of a function that takes two parameters and returns their sum:

def add_numbers(a, b):
    sum = a + b
    return sum

In this example, we define a function called add_numbers that takes two parameters a and b. The function then calculates their sum and returns the result using the return keyword. Once the function is defined, we can call it and pass in arguments to the parameters:

result = add_numbers(5, 7)
print(result) # Output: 12

In this example, we call the add_numbers function and pass in the values 5 and 7 as arguments to its parameters a and b. The function then calculates the sum of these two values and returns 12. Finally, we assign the return value of the function to a variable called result and print it to the console.

Calling Functions

After defining a function, you can call it to perform a specific task or operation. When a function is called, the code inside the function's block gets executed, and the function may return a value if specified.

a. Syntax

The syntax for calling a function is:

function_name(argument1, argument2, ...)

Here, function_name is the name of the function that you want to call and argument1, argument2, and so on, are the arguments that you want to pass to the function. If the function doesn't take any arguments, you can simply call it using the following syntax:

function_name()

This will call the function with no arguments.

b. Positional Arguments

Positional arguments are the most common type of arguments passed to a function in Python. In this type of argument passing, the arguments are matched based on their position in the function call. The first argument in the function call is matched with the first parameter in the function definition, the second argument with the second parameter, and so on.

For example, let's say we have a function called add_numbers that takes two arguments, num1 and num2, and returns their sum:

def add_numbers(num1, num2):
    return num1 + num2

To call this function, we can pass two values as positional arguments:

result = add_numbers(5, 10)
print(result)   # Output: 15

In this example, the first argument 5 is matched with the first parameter num1, and the second argument 10 is matched with the second parameter num2. The function returns their sum 15, which is then printed to the console.

c. Keyword Arguments

Keyword arguments are arguments passed to a function that are preceded by the name of the parameter to which they correspond. In this case, the order in which the arguments are passed doesn't matter, as long as each argument is assigned to the correct parameter.

Here is an example of a function call with keyword arguments:

def greet(name, message):
    print(f"{message}, {name}!")

greet(message="Hello", name="Rocky")

Output:

Hello, Rocky!

In the example above, we pass the arguments message and name to the function greet using the syntax parameter_name=value. This allows us to pass arguments out of order without any confusion, and makes our code more readable.

d. Default Values

When defining a function, you can specify default values for some or all of the parameters. These default values will be used when the function is called without passing any values for those parameters.

Here's an example:

def greet(name, greeting='Hello'):
    print(greeting + ', ' + name)

greet('Alice')          # prints "Hello, Alice"
greet('Bob', 'Hi')      # prints "Hi, Bob"

In the example above, greeting is an optional parameter with a default value of 'Hello'. When we call greet('Alice'), the function uses the default value for greeting and prints "Hello, Alice". When we call greet('Bob', 'Hi'), the function uses the value 'Hi' for greeting and prints "Hi, Bob".

Examples

# Defining a function to calculate the sum of two numbers
def add_numbers(num1, num2):
    sum = num1 + num2
    return sum

# Calling the function with positional arguments
result1 = add_numbers(5, 10)
print("Result 1:", result1)

# Calling the function with keyword arguments
result2 = add_numbers(num1=7, num2=3)
print("Result 2:", result2)

# Calling the function with one argument and using the default value for the other
result3 = add_numbers(num1=2)
print("Result 3:", result3)

Output:

Result 1: 15
Result 2: 10
Result 3: TypeError: add_numbers() missing 1 required positional argument: 'num2'

In this example, we defined a function add_numbers() that takes two parameters and returns their sum. We then called the function three times with different combinations of arguments:

  • The first call uses positional arguments to pass 5 and 10 as the values for num1 and num2, respectively.

  • The second call uses keyword arguments to pass 7 and 3 to num1 and num2, respectively.

  • The third call only passes a value for num1, which uses the default value of 0 for num2. However, this call results in a TypeError because the function requires two arguments and we only provided one.

Function Arguments

Functions are an essential aspect of Python programming. They allow us to create blocks of code that can be reused throughout our code, which makes our code more efficient, modular, and easier to maintain. In Python, we can define functions that accept arguments to take input values and use them to perform some tasks.

There are several types of function arguments in Python, and we will explore them in detail. We will learn how to define functions with arguments and how to call them with different types of arguments, including positional arguments, keyword arguments, and default arguments.

Positional Arguments

When it comes to passing arguments to a function, positional arguments are one of the most commonly used methods in Python. As the name suggests, these arguments are passed based on their position or order, from left to right, in the function call.

In other words, the first argument passed in the function call corresponds to the first parameter defined in the function definition, the second argument corresponds to the second parameter, and so on. Positional arguments are useful when you want to pass a fixed number of arguments to a function, and the order in which they are passed is important.

Let's take an example of a function that calculates the area of a rectangle using its length and breadth as arguments. The length and breadth will be our positional arguments, and their order will be important.

def calculate_area(length, breadth):
    area = length * breadth
    return area

# Calling the function with positional arguments
result = calculate_area(5, 6)
print("The area of the rectangle is:", result)

In this example, we define a function called calculate_area that takes two positional arguments: length and breadth. We then call the function and pass the values 5 and 6 as arguments, respectively. These values are assigned to length and breadth in the function definition, and the area of the rectangle is calculated using the formula area = length * breadth. Finally, the calculated area is returned and printed to the console.

Keyword Arguments

When calling a function, the values for the parameters can be passed by specifying the parameter names, rather than relying on their positions. These are called keyword arguments. Using keyword arguments can make the function call more readable and easier to understand, especially when dealing with functions that have many parameters.

Keyword arguments are particularly useful when the function has default parameter values. By using keyword arguments, you can selectively override the default values for only the parameters that need to be changed.

For example, let's consider a function that calculates the volume of a rectangular prism, which has three parameters: length, width, and height. We can use keyword arguments to specify the values for these parameters, like this:

def calculate_volume(length, width, height):
    return length * width * height

# calling the function with keyword arguments
volume = calculate_volume(length=3, width=4, height=5)

Here, we've specified the values for the length, width, and height parameters using keyword arguments. This makes the function call more readable, and we can easily see what values we're passing in.

Using keyword arguments can also make the function call more robust to changes in the function signature. If we add or remove a parameter from the function, the function call will still work as long as the parameter names remain the same. This is in contrast to positional arguments, where adding or removing a parameter can break the function call if the argument positions are not adjusted accordingly.

Default Arguments

Have you ever found yourself repeatedly passing the same argument to a function while calling it? In Python, you can avoid that repetition by setting default values for function arguments. In this way, if an argument is not specified while calling the function, it takes the default value that was defined in the function definition. This feature is known as default arguments. Default arguments are used to provide a default value to a function parameter in case a value is not provided when the function is called. This makes it easier to write functions that can handle different input values without requiring the user to provide a value for every parameter.

Syntax

To specify a default value for a parameter, you simply include an equal sign and the default value after the parameter name in the function definition. For example:

def greet(name, message="Hello"):
    print(message, name)

In this example, the message parameter has a default value of "Hello". If the user does not provide a value for message when they call the greet function, it will default to "Hello". If the user does provide a value for message, that value will be used instead.

Example

def greet(name, message="Hello"):
    print(message, name)

greet("John") # prints "Hello John"
greet("Jane", "Hi there") # prints "Hi there Jane"

In this example, we define a function greet that takes two parameters: name and message. If the user does not provide a value for message, it defaults to "Hello". We then call the greet function twice, once with only a value for name, and once with values for both name and message. The function uses the default value of "Hello" for message in the first call, and the user-provided value of "Hi there" in the second call.

Variable-Length Arguments (Arbitrary Arguments)

Variable-length arguments, also known as arbitrary arguments, allow a function to accept any number of arguments. In Python, there are two types of variable-length arguments: *args and **kwargs.

args is used to pass a variable number of non-keyword arguments to a function. It is represented by an asterisk () before the parameter name in the function definition. The function treats the arguments as a tuple.

kwargs is used to pass a variable number of keyword arguments to a function. It is represented by two asterisks () before the parameter name in the function definition. The function treats the arguments as a dictionary.

Using variable-length arguments makes the function more flexible, as it allows the user to pass any number of arguments without having to change the function definition.

Here is an example of a function that accepts variable-length arguments:

def print_args(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)
    
print_args(1, 2, 3, a=4, b=5)

Output:

Positional arguments: (1, 2, 3)
Keyword arguments: {'a': 4, 'b': 5}

In this example, the print_args() function accepts both positional arguments (1, 2, 3) and keyword arguments (a=4, b=5). The function then prints the positional arguments as a tuple and the keyword arguments as a dictionary.

Note that the order of the parameter names does not matter, but *args must come before **kwargs in the function definition.

Keyword Variable-Length Arguments.

Keyword variable-length arguments, also known as kwargs, allow you to pass a variable number of keyword arguments to a function. This means you can pass any number of arguments to a function, as long as they are all named.

The syntax for defining kwargs uses two asterisks (**) before the parameter name. Within the function, kwargs are treated as dictionaries, with the keyword argument names as keys and their corresponding values as values.

Kwargs are especially useful when you want to create a function with a lot of optional arguments, as it allows you to pass them in a flexible manner without having to define all possible argument combinations in the function definition.

Here's an example of using kwargs in a function definition:

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

In this example, the print_kwargs function takes any number of keyword arguments and prints them out in the format key: value.

Here's how you can call the print_kwargs function with kwargs:

print_kwargs(name="Rocky", age=30, location="New York")

This will output:

name: Rocky
age: 30
location: New York

As you can see, the kwargs are passed as keyword arguments, with the argument name followed by the value. The function then prints out each key-value pair in the kwargs dictionary.

Anonymous Functions (Lambda Functions)

Lambda functions, also known as anonymous functions, are functions without a name. These functions are small and typically used when you need to pass a function as an argument to another function. Lambda functions are defined using the keyword "lambda" and can take any number of arguments, but can only have one expression.

Lambda functions are often used with built-in functions such as filter(), map(), and reduce(). They are also commonly used in functional programming paradigms.

The syntax of a lambda function is:

lambda arguments: expression

Here, "arguments" are the parameters that the lambda function will accept and "expression" is the operation that the function will perform.

Let's take an example to understand it better:

# Define a lambda function to add two numbers
add_numbers = lambda x, y: x + y

# Call the lambda function
result = add_numbers(10, 20)

# Print the result
print(result)

In the example above, we defined a lambda function that takes two arguments and adds them together. We then called the lambda function with arguments 10 and 20 and stored the result in the variable "result". Finally, we printed the result, which is 30.

Lambda functions are a powerful tool in Python programming, but it's important to use them wisely and only when necessary.

Return Statement

The return statement is a fundamental concept in Python functions. It is used to return a value or an expression from a function to the calling program. When a return statement is encountered in a function, it immediately terminates the execution of the function and returns the specified value to the calling program.

The return statement in Python can be used with or without a value. If you don't specify a value after the return keyword, it returns None by default.

In Python, a function can return multiple values separated by commas. When multiple values are returned, they are packed into a tuple.

Example:

def sum_and_difference(a, b):
    sum = a + b
    difference = a - b
    return sum, difference

result = sum_and_difference(10, 5)
print(result)  # Output: (15, 5)

In the example above, the sum_and_difference function returns two values: the sum and the difference of the two input arguments. The function is called with the arguments 10 and 5, and the returned values are assigned to the variable result. The print statement outputs the tuple containing the two returned values.

It's important to note that the return statement can only be used within a function. If you try to use it outside of a function, you will get a syntax error.

Global and Local Variables

Global and local variables are an essential aspect of programming in any language, and Python is no exception. In Python, a variable's scope determines where the variable can be accessed within the code. There are two types of variables in Python - global variables and local variables.

Global variables are the variables that are declared outside a function or a block of code, and they can be accessed from anywhere within the code. Local variables, on the other hand, are declared inside a function or a block of code and can only be accessed within that block.

When a variable is declared inside a function, it is by default considered to be a local variable. If we want to use the same variable name globally, we can use the 'global' keyword to indicate that the variable is a global variable.

a. Global variable

In Python, a variable defined outside a function is a global variable. Global variables are accessible from any part of the code, including inside functions. However, if a function tries to modify a global variable, it must first declare the variable as global using the global keyword. Otherwise, the function will create a new local variable with the same name as the global variable, which will have no effect on the original global variable.

Here's an example:

x = 10 # global variable

def my_func():
    global x
    x += 5 # modifies the global variable
    print(x) # prints 15

my_func()
print(x) # prints 15

In this example, we have a global variable x with a value of 10. The function my_func() modifies the value of x by adding 5 to it and then prints the new value. When we call the function, it prints 15. After the function call, we print the value of x again, which is still 15 because we modified the global variable inside the function.

Without the global keyword, the function would create a new local variable x that has no relation to the global variable x:

x = 10 # global variable

def my_func():
    x = 5 # creates a new local variable x
    print(x) # prints 5

my_func()
print(x) # prints 10

In this example, the function my_func() creates a new local variable x with a value of 5. When we call the function, it prints the local variable, which is 5. After the function call, we print the value of the global variable x, which is still 10 and was not affected by the function call.

b. Local variables

In Python, a variable defined inside a function has local scope, which means it can only be accessed inside that function. Local variables can be defined with the same name as a global variable, but they are entirely different variables.

Here's an example:

x = 5  # global variable

def my_function():
    x = 3  # local variable with the same name as global variable
    print("Local variable x:", x)

my_function()
print("Global variable x:", x)

Output:

Local variable x: 3
Global variable x: 5

In the example above, x is a global variable with a value of 5. Inside the my_function function, a local variable named x is defined with a value of 3. When the function is called, the local variable x is printed, which outputs 3. After the function call, the global variable x is printed, which outputs 5.

It's important to note that a local variable cannot be accessed outside of the function in which it is defined. If you try to access a local variable outside of its function, a NameError will occur.

Recursion

Recursion is a programming technique where a function calls itself repeatedly until a specific condition is met. In Python, as in many other programming languages, recursion is a powerful tool that can be used to solve complex problems.

The basic idea behind recursion is to break down a problem into smaller subproblems that are easier to solve, and then combine the results to solve the original problem. Recursive functions have a few key components:

  1. Base case: The simplest form of the problem that can be solved directly without recursion.

  2. Recursive case: The problem is broken down into smaller subproblems that are solved recursively, until the base case is reached.

Recursion can be used to solve a wide variety of problems, such as traversing complex data structures like trees and graphs, searching for patterns in data, and generating fractal images. However, it's important to use recursion judiciously, since it can be computationally expensive and lead to stack overflow errors if not used properly.

Example:

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

This is a function that calculates the factorial of a number using recursion. The base case is when n is equal to 1, and the function simply returns 1. Otherwise, it calculates n times the factorial of n-1, which is done by calling the function recursively.

For example, factorial(5) would return 120 because 5 x 4 x 3 x 2 x 1 = 120.

Function Decorators

Function decorators are a way to add extra functionality to existing functions in Python. Decorators allow you to modify or extend the behavior of a function without changing its source code. They are a powerful tool for code reuse and can help you write more efficient and maintainable code.

In Python, a decorator is a special type of function that takes another function as its argument and returns a new function with some additional functionality. The syntax for using decorators is simply to place the decorator function name above the function definition you want to decorate.

Here's an example of a simple decorator that adds a timer to a function:

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Elapsed time: {end_time - start_time:.2f} seconds")
        return result
    return wrapper

@timer
def my_function():
    time.sleep(1)
    return "Hello, World!"

my_function()

In this example, the timer function is a decorator that takes another function func as an argument. It defines a new function wrapper that wraps the original function func with the timer functionality. The wrapper function uses the time module to measure the elapsed time of the original function, then prints the elapsed time to the console.

The @timer decorator syntax is used to decorate the my_function function with the timer decorator. When my_function is called, it is actually the decorated wrapper function that gets executed. The wrapper function calls the original my_function and returns its result, while also printing the elapsed time.

Function decorators can be used for a variety of purposes, such as caching, logging, authentication, and more. They are a powerful and flexible tool for extending the functionality of your Python code.

Last updated