A Comprehensive Guide to Python Functions Definition and Lambdas

nikita1659244 30 Aug, 2024
9 min read

Introduction

We have been discussing Python and its versatility. Now is the time to understand another functionality of this powerful programming language: it enhances code efficiency and readability. Maintaining the modularity of your code logic while working on a production-level program is important.

Python Function definition allows the developers to achieve this by encapsulating the codes. On the other hand, lambda functions provide a compact way to define simple functions in Python.

In this guide, we will explore the syntaxes, usages, and best practices for both types of Python functions to build a solid foundation for leveraging these tools in your Python projects in the industry. Whether you want to break complex tasks into simpler functions or utilize lambda functions for concise operations, these methods will help you write efficient code.

To refresh your Python basic to advance, go through these –

  1. Comprehensive Guide to Advanced Python Programming- Link
  2. Comprehensive Guide to Python Built-in Data Structures – Link
  3. Fundamentals of Python Programming for Beginners- Link
Python and Lambda Functions

What is a Function?

A function in Python is a reusable block of code that performs a specific task depending on the program’s logic. They can take inputs (known as parameters or arguments), perform certain operations, and return outputs.

Functions are really helpful in organizing code, making it more readable, maintainable, and efficient in production.

Python Function uses two Most Important Principles:

  1. Abstraction: This principle makes the function hide the details of the complex implementation while showing only the essential features (i.e., whatever is returned as output).
  2. Decomposition: This principle involves breaking down a big task into smaller, more manageable function blocks to avoid redundancy and facilitate easier debugging.

Syntax:

The function syntax involves two things:

  • Function Declaration

In this part, you’ll write a logic, including a docstring, using the `def` keyword.

def function_name(paramters):
       """
       doc-string
       """
       function logic (body)
       return output
  • Function Calling

The above function does not return any output on its own. To print the output on the screen, you need to call the function using this syntax.

function_name(arguments)

Let’s explore an example of how to create a function.

Creating Function 

Now, let’s create our first function, including a docstring.

# Function body
def is_even(num:int):
  """
  Check if a number is even or odd.
  Parameters:
  num (int): The number to check.
  Returns:
  str: "even" for the even number and, "odd" if the number is odd.
  """
  # Function logic
  if type(num) == int:
    if num % 2 == 0:
      return "even"
    else:
      return "odd"
  else:
    return "Function needs an integer aruguement"
# Calling function
for i in range(1,11):
  print(i, "is", is_even(i))

Output

1 is odd

2 is even

3 is odd

4 is even

5 is odd

6 is even

7 is odd

8 is even

9 is odd

10 is even

How do you run documentation?

You can use `.__doc__` to access the docstring of your function (or any built-in function, which we have discussed here).

print(is_even.__doc__)

Output

Check if a number is even or odd.

Parameters:

num (int): The number to check.

Returns:

str: "even" for the even number and, "odd" if the number is odd.

To Note:

Programmers often confuse the parameter/s and the argument/s and use them interchangeably while speaking. But let’s understand the difference between them so that you never get into this dilemma.

  • Parameter: A parameter is a variable named in the function or method definition (in `class`). It acts as a placeholder for the data the function will use in the future.
  • Argument: The actual value is passed to the function or method when it is called to return the output according to the function logic.

Types of Arguments in Python

Due to their versatility, Python functions can accept different types of arguments, providing flexibility in how to call them.

The main types of arguments are:

  • Default Arguments
  • Positional Arguments
  • Keyword Arguments
  • Arbitrary Positional Arguments (*args)
  • Arbitrary Keyword Arguments (**kwargs)

Let’s understand them one by one:

1. Default Arguments

  • Arguments that assume a default value while writing the function, if a value is not provided during the function call.
  • Useful for providing optional parameters when user doesn’t enter the value.
    def greet(name, message="Hello"):
        return f"{message}, {name}!"
    print(greet("Nikita"))
    print(greet("Nikita", "Hi"))

    Outputs

    Hello, Nikita!
    Hi, Nikita!

    2. Positional Arguments

    • Arguments passed to a function in a specific order are called positional arguments.
    • The order in which the arguments are passed matters, or else it may return the wrong output or error.
      def add(a, b):
          return a + b
      
      print(add(2, 3))

      Output

      Outputs: 5

      3. Keyword Arguments

      • Arguments that are passed to a function using the parameter name as a reference are known as Keyword Arguments.
      • The order doesn’t matter herein, as each argument is assigned to the corresponding parameter.
      def greet(name, message):
          return f"{message}, {name}!"
      print(greet(message="Hello", name="Nikita"))

      Output

      Outputs: Hello, Nikita!

      4. Variable-Length Arguments

      `*args` and `**kwargs` are special python keywords that are used to pass the variable length of arguments to function.

      • Arbitrary Positional Arguments (*args): This allows a function to accept any number of non-keyword positional arguments.
      def sum_all(*args):
          print(type(args), args)
          return sum(args)
      print(sum_all(1, 2, 3, 4))

      Output

      <class 'tuple'> (1, 2, 3, 4)
      # 10
      • Arbitrary Keyword Arguments (**kwargs): This allows a function to accept any number of keyword arguments.
      def print_details(**kwargs):
          for key, value in kwargs.items():
              print(f"{key}: {value}")
      print_details(name="Nikita", age=20)

      Output

      name: Alice
      age: 30

      Note: Keyword arguments mean that they contain a key-value pair, like a Python dictionary.

      Point to remember

      The order of the arguments matters while writing a function to get the proper output:

        def function_name(parameter_name, *args, **kwargs):
        """
        Logic
        """

      Types of Functions in Python

      There are several types of functions Python offers the developers, such as:

      Function Type Description Example
      Built-in Functions Predefined functions available in Python. print(), len(), type()
      User-Defined Functions Functions created by the user to perform specific tasks. def greet(name):
      Lambda Functions Small, anonymous functions with a single expression. lambda x, y: x + y
      Recursive Functions Functions that call themselves to solve a problem. def factorial(n):
      Higher-Order Functions Functions that take other functions as arguments or return them. map(), filter(), reduce()
      Generator Functions Functions that return a sequence of values one at a time using yield. def count_up_to(max):

      Functions in Python are the 1st Class Citizen

      I know this is a very heavy statement if you’ve never heard it before, but let’s discuss it.

      Functions in Python are entities that support all the operations generally available to other objects, such as lists, tuples, etc.

      Being first-class citizens means functions in Python can:

      • Be assigned to variables.
      • Be passed as arguments to other functions.
      • Be returned from other functions.
      • Be stored in data structures.

      This flexibility allows for powerful and dynamic programming.

      type() and id() of Function

      By now, you may be excited to know about the function’s type() and id(). So, let’s code it to understand better:

      def sum(num1, num2):
        return num1 + num2
      print(type(sum))
      print(id(sum))

      Output

      <class 'function'>

      134474514428928

      Like other objects, this function also has a class of functions and an ID address where it is stored in memory.

      Reassign Function to the Variable

      You can also assign a function to a variable, allowing you to call the function using that variable.

      x = sum
      print(id(x))
      x(3,9)

      Output

      134474514428928

      12

      Note: x will have the same address as sum.

      Functions Can Also Be Stored in the Data Structures

      You can also store functions in data structures like lists, dictionaries, etc., enabling dynamic function dispatch.

      l1 = [sum, print, type]
      l1[0](2,3)
      # Calling function inside of a list

      Output

      5

      Functions are Immutable data types

      Let’s store a function `sum` in a set to prove this. As set will never allow mutable datatypes. 

      s = {sum}
      s 

      Output

      {<function __main__.sum(num1, num2)>}

      Since we got an output, this confirms that the set is the immutable data types.

      Functions Can Also Be Passed as Arguments to Other Functions

      You can also pass functions as arguments to other functions, enabling higher-order functions and callbacks.

      def shout(text):
          return text.upper()
      def whisper(text):
          return text.lower()
      def greet(func, name):
          return func(f"Hello, {name}!")
      print(greet(shout, "Nikita"))  # Outputs: HELLO, NIKITA!
      print(greet(whisper, "Nikita"))  # Outputs: hello, nikita

      Output

      HELLO, NIKITA!
      hello, nikita

      We will cover higher-order functions in detail later in this article. So, stay tuned until the end!

      Functions Can Also Be Returned from Other Functions

      A function can also return other functions, allowing the creation of multiple functions or decorators.

      def create_multiplier(n):
          def multiplier(x):
              return x * n
          return multiplier
      double = create_multiplier(2)
      print(double(5))  # Outputs: 10
      triple = create_multiplier(3)
      print(triple(5))  # Outputs: 15

      Outputs

      10
      15

      Advantages of using Functions

      Python functions offer 3 major advantages, such as 

      • Code Modularity: Functions allow you to encapsulate the logic within named blocks, breaking down complex problems into smaller, more organized pieces.
      • Code Readability: Functions make code much cleaner and easier for others (or yourself) to understand while reviewing and debugging it in the future.
      • Code Reusability: Once the logic is created, it can be called multiple times in a program, reducing code redundancy.

      Also read: What are Functions in Python and How to Create Them?

      What is a Lambda Function?

      A lambda function, also called an inline function is a small anonymous function. It can take any number of arguments, but can only have one-line expression. These functions are particularly useful for a short period.  

      Syntax:

      Let’s check some examples:

      1. Lambda Function with one variable

      # square a value
      func = lambda x : x**2
      func(5)

      Output

      25

      2. Lambda Function with two variables

      # Subtracting a value
      func  = lambda x=0, y=0: x-y
      func(5)

      Output

      5

      3.  Lambda Function with `if-else` statement

      # Odd or Even
      func = lambda x : "even" if x%2==0 else "odd"
      func(1418236418)

      Output

      'even'

      Lambda functions vs. Normal functions 

      Feature Lambda Function Normal Function
      Definition Syntax Defined using the lambda keyword Defined using the def keyword
      Syntax Example lambda x, y: x + y def add(x, y):\n return x + y
      Function Name Anonymous (no name) Named function
      Use Case Short, simple functions Complex functions
      Return Statement Implicit return (single expression) Explicit return
      Readability Less readable for complex logic More readable
      Scoping Limited to a single expression Can contain multiple statements
      Decorators Cannot be decorated Can be decorated
      Docstrings Cannot contain docstrings Can contain docstrings
      Code Reusability Typically used for short, throwaway functions Reusable and maintainable code blocks

      Why use the Lambda Function?

      Lambda functions do not exist independently. The best approach to using them is with higher-order functions (HOF) like map, filter, and reduce.

      While these functions have a limited scope compared to regular functions, they can offer a succinct way to streamline your code, especially in sorting operations.

      Also read: 15 Python Built-in Functions which You Should Know while learning Data Science

      What are Higher Order Functions(HOF) in Python?

      A higher-order function, commonly known as an HOF, can accept other functions as arguments, return functions, or both.

      For instance, this is how you can use a HOF:

      # HOF
      def transform(lambda_func, list_of_elements):
        output = []
        for i in L:
          output.append(f(i))
        print(output)
      L = [1, 2, 3, 4, 5]
      # Calling function
      transform(lambda x: x**2, L)

      Output

      [1, 4, 9, 16, 25]

      The main function in this code snippet is to take a lambda function and a list of elements.

      Note: As per the problem statement, you can apply any specific logic using this lambda function.

      Now, let’s dive deep into the Types of HOFs.

      What are 3 HOF in Python?

      Here are 3 HOF in Python:

      1. map()

      It applies a given function to each item of an iterable (e.g., list, dictionary, tuple) and returns a list of the results.

      For instance, 

      # Fetch names from a list of dict
      people = [
          {"name": "Alice", "age": 25},
          {"name": "Bob", "age": 30},
          {"name": "Charlie", "age": 35},
          {"name": "David", "age": 40}
      ]
      list(map(lambda person: person["name"], people))

      Output

      ['Alice', 'Bob', 'Charlie', 'David']

      2. filter()

      It creates a list of elements for which a given function returns `True`, similar to any filter operation in different programming languages.

      For instance, 

      # filter: fetch names of people older than 30
      filtered_names = filter(lambda person: person["age"] > 30, people)
      filtered_names_list = map(lambda person: person["name"], filtered_names)
      print(list(filtered_names_list))

      Output

      ['Charlie', 'David']

      3. reduce()

      It applies a function cumulatively to the items of an iterable, reducing it to a single value.

      For instance, 

      # reduce: concatenate all names into a single string
      concatenated_names = reduce(lambda x, y: x + ", " + y, map(lambda person: person["name"], people))
      print(concatenated_names)

      Output

      Alice, Bob, Charlie, David

      Note: All of these functions expect a lambda function and an iterable.

      Conclusion

      To conclude this article on Python Functions Definition and Lambda Functions, I would say that if you aim to write robust and scalable code, it is really important to master both of these functionalities to work in real-life industries.

      Additionally, this practice helps in writing cleaner code and enhances collaboration throughout the team, as other programmers can easily understand and use the predefined functions to reduce redundancy.

      Frequently Asked Questions

      Q1. What is Function Definition in Python?

      Ans. Function definitions, often referred to as normal functions in Python, allow programmers to encapsulate code into reusable blocks to promote modularity, enhance readability, and make it easier to debug.

      Q2. What is the Lambda Function in Python?

      Ans. Lambda functions, often referred to as anonymous or inline functions, provide a compact way to define simple functions as needed for a short period, such as in sorting operations or within higher-order functions like map(), filter(), and reduce().

      Q3. What is the difference between map(), filter(), and reduce()?

      Ans. Here’s the difference:
      `map()`: Applies a given function to each item of an iterable and returns a list of the results.
      `filter()`: creates a list of elements for which a given function returns `True`.
      `reduce()`: Applies a function cumulatively to the items of an iterable, reducing it to a single value.  

      nikita1659244 30 Aug, 2024

      Hi-ya!!! 👋 I'm Nikita Prasad Data Analyst | Machine Learning and Data Science Practitioner ↪️ Checkout my Projects- GitHub: https://github.com/nikitaprasad21 Know thy Author: 👩🏻‍💻 As an analyst, I am eager to gain a deeper understanding of the data lifecycle with a range of tools and techniques, distilling down data for actionable takeaways using Data Analytics, ETL, Machine Learning, NLP, Sentence Transformers, Time-series Forecasting and Attention to Details to make recommendations across different business groups. Happy Learning! 🚀🌟

      Frequently Asked Questions

      Lorem ipsum dolor sit amet, consectetur adipiscing elit,