SomeQuiz

Course

Python Functions

Co-Founder

Top Contributor

Nuwan Perera

@nuwanperera.me

Functions are a fundamental concept in programming, and Python provides a powerful and flexible way to define and use functions. In Python, a function is a block of reusable code that performs a specific task. Functions help in organizing code, promoting code reusability, and improving the overall structure of a program.

Types of Functions

  1. Built-in Functions
  2. User-Defined Functions
  3. Lambda Functions
  4. Higher-Order Functions
  5. Recursive Functions

Built-in Function

Python provides a rich set of built-in functions that are readily available for use without requiring any additional imports or custom implementations. These functions cover a wide range of operations, such as mathematical calculations, string manipulations, list operations, and much more. Some popular built-in functions include print(), len(), input(), range(), sum(), and str(). These functions serve as the foundation for many programming tasks and simplify common operations.

Built-in Functions used in Python

FunctionDescription
abs(x)Returns the absolute value of a number x.
all(iterable)Returns True if all elements in the iterable are True, or if the iterable is empty.
any(iterable)Returns True if any element in the iterable is True. If the iterable is empty, it returns False.
bin(x)Converts an integer x to a binary string prefixed with "0b".
chr(i)Returns a string representing a character whose Unicode code point is i
dir(object)Returns a list of names in the current local scope or the attributes of an object (if provided).
enumerate(iterable, start=0)Returns an iterator of tuples containing indices and values from the iterable.
eval(expression, globals=None, locals=None)Evaluates a string as a Python expression and returns the result.
filter(function, iterable)Constructs an iterator from elements of the iterable for which the function returns True.
format(value, format_spec)Returns a formatted version of a specified value, according to a specified format.
help(object)Displays help information or documentation for the specified object (if provided).
input(prompt)Reads a line of input from the user as a string.
len(s)Returns the length (the number of items) of an object, such as a string, list, or tuple.
max(iterable, *args, key=None)Returns the largest item in an iterable or the largest of two or more arguments.
min(iterable, *args, key=None)Returns the smallest item in an iterable or the smallest of two or more arguments.
open(file, mode='r')Opens a file and returns a corresponding file object.
range(start, stop, step)Returns an iterable of numbers from start to stop (exclusive), incrementing by step.
round(number, ndigits)Returns the number rounded to ndigits decimal places (or to the nearest whole number if ndigits is not specified).
sorted(iterable, *, key=None, reverse=False)Returns a new sorted list from the items in the iterable.
str(object)Returns a string version of the specified object.
sum(iterable, start=0)Returns the sum of all items in an iterable, starting from the optional start value.
type(object)Returns the type of an object.
zip(*iterables)Returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the iterables.

Furthermore Reading about the Built-in function 👉 Python Documentation

User-Defined Functions

User-defined functions are functions created by the programmer to perform specific tasks. These functions are defined using the def keyword, and they offer flexibility and reusability. By defining your own functions, you can modularize your code and make it more organized.

Defining a User-Defined Function

In Python, user-defined functions are created using the def keyword followed by the function name, parentheses, and a colon. The function body is indented below the function definition.

  • Syntax

  • Let's see an example,
def welcome_msg():
  print('Welcome to SomeQuiz!')

Here, we have created a function named welcome_msg(). It simply prints the text Hello World!. This function doesn't have any arguments and doesn't return any values.

Calling a Function

To use or invoke a user-defined function, you simply write the function name followed by parentheses. For example, to call the welcome_msg function defined above, you would write,

def welcome_msg():
  print('Welcome to SomeQuiz!')
 
# call the function
welcome_msg()
  • Output

  • Here's how the program works

Function Parameters

Functions in Python can accept input values called parameters or arguments. Parameters allow you to pass data to functions, making them more versatile. Parameters are defined inside the parentheses in the function definition.

If we create a function with parameters, we need to pass the corresponding values while calling them.

# Function with two parameters
def introducer(name, age):
 
  print(f'Hello, My name is {name} and I\'m {age} years old.')
 
# Function call with two Arguments
introducer("Nuwan", 19)
  • Output

  • Here's how the program works

We can also call the function by mentioning the parameter name as;

introducer(name= "Nuwan", age= 19)

Default Arguments

Python allows defining default values for function parameters. Default values are used when a parameter is not provided during function invocation. This provides flexibility and allows functions to have optional parameters.

def registration(name, username="user", password=123):
  
  # Multi line String
  msg = (f"""Hello {name}!
Your Information:
User Name: {username}
Password: {password}""")
 
  print(msg)
 
registration("Nuwan", "user@%$") # Only giving Two arguments
  • Output

  • Here's how the program works

keep the following points in mind while calling functions:

  1. In the case of passing the keyword arguments, the order of arguments is important.
  2. There should be only one value for one parameter.
  3. The passed keyword name should match with the actual keyword name.
  4. In the case of calling a function containing non-keyword arguments, the order is important.

Return Statement

In addition to printing output, functions in Python can also return values using the return statement. The return statement allows a function to send a value back to the caller.

def add(a, b):
    
    sum = a + b
 
    return sum
 
x = add(3, 5)
print(x)
  • Output
8
  • Here's how the program works

Scope of Variables

Variables defined inside a function have local scope, meaning they are only accessible within that function. This allows for the isolation of variables and prevents naming conflicts. However, variables defined outside any function have global scope and can be accessed from any part of the code.

 
def local_variables():
    
    # This variable is inside the function.
    a = 20
    print("Local variable:", a)
 
local_variables()
 
print("Global variable", a)
  • Output

Create a global variable in Python

Defining and accessing Python global variables.

def local_variables():
 
    print("Local variable:", a)
 
a = 10
local_variables()
 
print("Global variable:", a)
  • Output
Local variable: 10
Global variable: 10

The global Keyword

The global keyword is used inside a function to indicate that a variable is a global variable, meaning it is defined in the global scope rather than the local scope of the function. When you assign a value to a variable inside a function without using the global keyword, Python assumes that variable is local to the function.

x = 10  # global variable
 
def modify_global():
    global x  # indicating that we want to use the global variable x
    x = 20  # modifying the value of the global variable
 
print(x)
modify_global()
print(x)
  • Output
10
20

we have a global variable x with an initial value of 10. Inside the modify_global function, we use the global keyword before referencing the variable x. This allows us to modify the global variable's value within the function. When we call the modify_global function and then print the value of x again, we can see that the global variable has been updated to 20.

It's important to note that using the global keyword is generally discouraged, as it can make code harder to reason about and maintain. It is often considered better practice to avoid modifying global variables within functions and instead pass values as arguments and return updated values.

Benefits of Using Functions

Code Reusable - We can use the same function multiple times in our program which makes our code reusable.

Code Readability - Functions help us break our code into chunks to make our program readable and easy to understand.

Lambda Functions

In addition to regular functions, Python also supports lambda functions, also known as anonymous functions. Lambda functions are concise and handy when you need a small function without a formal definition. Unlike regular functions, lambda functions are defined using the lambda keyword, without a function name, and are typically used when a function is required as an argument for another function.

  • Syntax

  • Example: Python lambda Function
# declare a lambda function
greet = lambda : print('Hello Coders!')
 
# call lambda function
greet()

We have defined a lambda function and assigned it to the greet variable.

When we call the lambda function, the print() statement inside the lambda function is executed.

  • Output
Hello Coders!

lambda Function with an Argument

# Lambda function
add_lambda = lambda a, b: a + b
 
result_lambda = add_lambda(3, 5)
print(result_lambda)
  • Output
8

Lambda functions are particularly useful when working with higher-order functions, such as map(), filter(), and reduce(). These functions expect a function as an argument, and lambda functions provide a concise way to define the required function on the fly.

numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # Output: [1, 4, 9, 16, 25]

In the example above, we use the map() function to apply the lambda function to each element of the numbers list. The lambda function squares each element, resulting in a new list of squared values.

Lambda functions are best suited for short and simple operations. For more complex operations, it's generally recommended to use regular functions to improve readability and maintainability.

Higher-Order Functions

A higher-order function is a function that takes one or more functions as arguments or returns a function as its result.

Understanding Higher-Order Functions

In Python, functions are treated as first-class objects, meaning they can be assigned to variables, passed as arguments to other functions, and returned as values from functions. This property allows us to create higher-order functions.

Higher-order functions provide a way to abstract common patterns and behaviors, making code more modular and reusable. They enable us to write functions that operate on other functions, allowing us to separate concerns and focus on the core logic of our programs.

Passing Functions as Arguments

One of the primary use cases for higher-order functions is passing functions as arguments. This enables us to customize the behavior of a function by providing different functions as arguments.

def apply_operation(operation, a, b):
    return operation(a, b)
 
def add(a, b):
    return a + b
 
def multiply(a, b):
    return a * b
 
result1 = apply_operation(add, 3, 5)
print(result1)  
 
result2 = apply_operation(multiply, 3, 5)
print(result2)  
  • Output
8
15

In the above code, we define the apply_operation function, which takes an operation function as an argument, along with two numbers. It then applies the provided operation to the numbers and returns the result.

We define two other functions, add and multiply, which perform addition and multiplication, respectively. By passing these functions as arguments to apply_operation, we can perform different operations on different numbers.

Returning Functions

Another powerful feature of higher-order functions is the ability to return functions. This allows us to create functions on the fly, based on certain conditions or calculations.

def create_multiplier(n):
    def multiply(x):
        return x * n
    return multiply
 
double = create_multiplier(2)
result1 = double(5)
print(result1)
 
triple = create_multiplier(3)
result2 = triple(5)
print(result2)
  • Output
10
15

In the above code, we define the create_multiplier function, which takes a number n and returns a new function multiply. The multiply function multiplies its argument x with n.

By calling create_multiplier with different values, we obtain different functions (double and triple) that multiply their arguments by the specified factor.

  • Here's how the program works

Using Higher-Order Functions in Practice

Higher-order functions are widely used in Python, especially in conjunction with built-in functions like map(), filter(), and reduce().

The map() function applies a given function to each item in an iterable and returns an iterator with the results. Similarly, the filter() function applies a given function to each item in an iterable and returns an iterator containing only the items for which the function returns True. The reduce() function applies a binary function to the items of an iterable, reducing them to a single value.

numbers = [1, 2, 3, 4, 5]
 
squared = list(map(lambda x: x**2, numbers))
print(squared)  # Output: [1, 4, 9, 16, 25]
 
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even)  # Output: [2, 4]

In the above code, we use the map() function to square each element of the numbers list and obtain a new list of squared values. Similarly, we use the filter() function to retrieve only the even numbers from the numbers list.

Recursive Functions

Python provides several powerful techniques for solving problems efficiently. One such technique is recursion, which allows a function to call itself, enabling elegant solutions to complex problems.

Understanding Recursion

Recursion is a programming concept where a function solves a problem by reducing it into smaller instances of the same problem until a base case is reached. It's like solving a puzzle by breaking it down into smaller pieces. Each recursive call brings us closer to the base case, eventually leading to a complete solution.

Recursive Functions in Python

To create a recursive function in Python, we need two essential components: a base case and a recursive call. The base case defines the condition under which the function stops calling itself and returns a result. The recursive call breaks down the problem into a smaller instance and calls itself to solve that smaller problem.

Calculating Factorials

Let's start with a classic example of calculating factorials using recursion. The factorial of a non-negative integer n (denoted as n!) is the product of all positive integers from 1 to n.

def factorial(n):
  # Base case: factorial of 0 or 1 is 1
  if n == 0 or n == 1:
    return 1
  else:
    # Recursive call: n! = n * (n-1)!
    return n * factorial(n - 1)
 
print(factorial(5))
  • Output
120

In this example, the base case occurs when n is 0 or 1. Any other value triggers the recursive call, where the function multiplies n by the factorial of (n-1). This continues until the base case is reached.

  • Here's how the program works

Our recursion ends when the number reduces to 1. This is called the base condition.

Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely.

  • The Python interpreter limits the depths of recursion to help avoid infinite recursions, resulting in stack overflows.

  • By default, the maximum depth of recursion is 1000. If the limit is crossed, it results in RecursionError. Let's look at one such condition.

def recursor(count):
    
    print(count)
    count +=1
    recursor(count)
 
recursor(1)
  • Output

Applying Recursion Wisely:

While recursion can provide elegant solutions, it's important to use it judiciously. Recursive functions can consume significant memory and time when used improperly. Here are a few guidelines to apply recursion wisely:

  • Identify the base case: Ensure there is a well-defined condition under which the function stops calling itself.
  • Break the problem into smaller instances: Reduce the problem into smaller, more manageable parts with each recursive call.
  • Ensure progress towards the base case: Verify that the function moves closer to the base case in each recursive call.
  • Avoid redundant calculations: Employ memoization techniques to cache results and prevent redundant computations.
  • Watch out for infinite recursion: Make sure the function always reaches the base case to avoid infinite loops.

Advantages of Recursion

  • Recursive functions make the code look clean and elegant.
  • A complex task can be broken down into simpler sub-problems using recursion.
  • Sequence generation is easier with recursion than using some nested iteration.

Disadvantages of Recursion

  • Sometimes the logic behind recursion is hard to follow through.
  • Recursive calls are expensive (inefficient) as they take up a lot of memory and time.
  • Recursive functions are hard to debug.