whenever life put's you in a tough situtation, never say why me! but, try me!

Advanced Function Concepts in Python

In this module, we will dive into advanced function concepts in Python that help make your code more powerful and flexible. These concepts include lambda functions, higher-order functions, decorators, and recursion.


Subtopic 1: Lambda Functions

A lambda function is a small anonymous function defined using the lambda keyword. It can have any number of arguments but only one expression, which is evaluated and returned.

Syntax:
lambda arguments: expression
Example: Basic Lambda Function
# Defining a lambda function to add two numbers
add = lambda a, b: a + b

result = add(3, 5)
print(result)  # Output: 8
Explanation:
  • The lambda function takes two arguments, a and b, and returns their sum.
  • It is used here to perform an addition in a compact form.
Example: Lambda Function in sorted()
# Using lambda with sorted to sort a list of tuples based on the second element
pairs = [(1, 2), (3, 1), (5, 7), (2, 6)]
sorted_pairs = sorted(pairs, key=lambda x: x[1])
print(sorted_pairs)  # Output: [(3, 1), (1, 2), (5, 7), (2, 6)]
Explanation:
  • lambda x: x[1] defines an anonymous function to access the second element of each tuple.
  • sorted() uses this lambda function to sort the list based on the second value.

Subtopic 2: Higher-Order Functions

A higher-order function is a function that either:

  • Takes one or more functions as arguments.
  • Returns a function as a result.
Example: Passing Functions as Arguments
# A function that accepts another function as an argument
def apply_function(f, x):
    return f(x)

# Using the square function as an argument
result = apply_function(lambda x: x ** 2, 5)
print(result)  # Output: 25
Explanation:
  • apply_function is a higher-order function that takes a function f and a value x as arguments.
  • In this example, we pass a lambda function to square the number.
Example: Returning a Function
# A function that returns another function
def multiplier(factor):
    return lambda x: x * factor

# Creating a function that multiplies by 3
multiply_by_3 = multiplier(3)
result = multiply_by_3(4)
print(result)  # Output: 12
Explanation:
  • multiplier is a higher-order function that returns a new function that multiplies its input by a specific factor.
  • The returned function multiply_by_3 multiplies the input by 3.

Subtopic 3: Decorators in Python

A decorator is a function that allows you to add functionality to an existing function without modifying its structure. Decorators are typically used to modify or extend the behavior of functions or methods.

Syntax for a Basic Decorator:
def decorator_function(original_function):
    def wrapper_function():
        # Add behavior before the function call
        print("Wrapper executed this before {}".format(original_function.__name__))
        return original_function()
    return wrapper_function
Example: Simple Decorator
def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

# Calling the decorated function
say_hello()

Output:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.
Explanation:
  • The my_decorator function takes a function func and wraps it with additional behavior (printing before and after the function call).
  • The @my_decorator syntax is used to apply the decorator to the say_hello function.
Example: Decorator with Arguments
def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(3)
def say_hi():
    print("Hi!")

say_hi()

Output:

Hi!
Hi!
Hi!
Explanation:
  • The repeat decorator takes an argument num_times and repeats the decorated function the specified number of times.

Subtopic 4: Recursion in Functions

Recursion occurs when a function calls itself in order to solve a problem. Recursive functions are particularly useful for tasks that can be broken down into smaller, similar tasks.

Example: Factorial using Recursion
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

print(factorial(5))  # Output: 120
Explanation:
  • The factorial function calls itself to calculate the factorial of n.
  • The base case is when n == 0, in which case the function returns 1.
Example: Fibonacci Sequence using Recursion
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(6))  # Output: 8
Explanation:
  • The fibonacci function calculates the Fibonacci number for n by calling itself with smaller values.

Tasks

  1. Task 1: Lambda Function for Squaring

    • Write a lambda function that returns the square of a given number.
    square = lambda x: x ** 2
    print(square(4))  # Output: 16
    
  2. Task 2: Higher-Order Function

    • Write a higher-order function that takes another function as an argument and applies it to a list of numbers.
    def apply_to_list(func, numbers):
        return [func(num) for num in numbers]
    
    result = apply_to_list(lambda x: x * 2, [1, 2, 3])
    print(result)  # Output: [2, 4, 6]
    
  3. Task 3: Decorator to Measure Execution Time

    • Write a decorator that measures the execution time of 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"Execution time: {end_time - start_time} seconds")
            return result
        return wrapper
    
    @timer
    def long_running_function():
        time.sleep(2)
    
    long_running_function()
    
  4. Task 4: Recursion for Sum of Natural Numbers

    • Write a recursive function to calculate the sum of the first n natural numbers.
    def sum_natural_numbers(n):
        if n == 0:
            return 0
        else:
            return n + sum_natural_numbers(n - 1)
    
    print(sum_natural_numbers(5))  # Output: 15
    
  5. Task 5: Decorator to Print Function Name

    • Write a decorator that prints the name of the function being called before the function executes.
    def print_function_name(func):
        def wrapper(*args, **kwargs):
            print(f"Function name: {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    
    @print_function_name
    def greet():
        print("Hello!")
    
    greet()
    
  6. Task 6: Recursive Fibonacci

    • Write a recursive function to compute the nth Fibonacci number.
    def fibonacci(n):
        if n <= 1:
            return n
        else:
            return fibonacci(n-1) + fibonacci(n-2)
    
    print(fibonacci(7))  # Output: 13
    

Summary of Concepts

  • Lambda Functions: Anonymous functions defined using the lambda keyword, useful for short-term functionality.
  • Higher-Order Functions: Functions that take other functions as arguments or return functions.
  • Decorators: Functions that modify the behavior of other functions without changing their code, often used for logging, timing, or permission checks.
  • Recursion: A method where a function calls itself to solve smaller subproblems, commonly used in problems like calculating factorials or Fibonacci numbers.

These advanced function concepts in Python will allow you to write more concise, efficient, and readable code.