Python Tutorial (21) - Functions

Time: Column:Python views:294

Functions in Python: Organizing Code for Reusability

Functions are organized, reusable blocks of code used to perform a single or related function. Functions improve the modularity of an application and promote code reuse. As you already know, Python provides many built-in functions, such as print(). However, you can also create your own functions, which are called user-defined functions.

Defining a Function

You can define a function with any desired functionality by following these simple rules:

  1. The function block starts with the def keyword, followed by the function name and parentheses ().

  2. Any input parameters or arguments must be placed within the parentheses. You can define parameters within the parentheses.

  3. The first statement in the function can optionally be a documentation string (docstring), used to describe the function.

  4. The function content starts with a colon : and is indented.

  5. A return [expression] statement ends the function, optionally returning a value to the caller. If there is no expression, the function returns None.

Functions in Python: Organizing Code for Reusability

Syntax

Python defines functions using the def keyword, and the general format is as follows:

def function_name(parameters):
    function_body

By default, the parameter values and parameter names are matched in the order they are declared.

Example

Let’s use a simple function to output "Hello World!":

#!/usr/bin/python3

def hello():
    print("Hello World!")

hello()

More Complex Example: Function with Parameters

Example (Python 3.0+)
This function compares two numbers and returns the larger one:

#!/usr/bin/python3

def max(a, b):
    if a > b:
        return a
    else:
        return b

a = 4
b = 5
print(max(a, b))

Output:

5


Example: Function to Calculate Area

#!/usr/bin/python3

# Function to calculate area
def area(width, height):
    return width * height

def print_welcome(name):
    print("Welcome", name)

print_welcome("Runoob")
w = 4
h = 5
print("width =", w, " height =", h, " area =", area(w, h))

Output:

Welcome Runoob
width = 4  height = 5  area = 20

Function Calls

Defining a function assigns it a name, specifies the parameters, and creates the code block structure. Once this is done, you can call the function from another function or directly from the Python prompt.

Here’s an example of calling the printme() function:

Example (Python 3.0+):

#!/usr/bin/python3

# Define the function
def printme(str):
    # Print any string passed in
    print(str)
    return

# Call the function
printme("I am calling a user-defined function!")
printme("Calling the same function again")


Output:

I am calling a user-defined function!
Calling the same function again


Argument Passing

In Python, types belong to objects, not to variables. Variables don’t have types; they are merely references to objects.

a = [1, 2, 3]
a = "Runoob"

In the code above, [1, 2, 3] is a list, and "Runoob" is a string, but a is just a reference to these objects. It can reference any type of object.

Mutable and Immutable Objects in Python

In Python, strings, tuples, and numbers are immutable objects, whereas lists, dictionaries, and other collections are mutable.

Immutable Types:

For example, when you assign a = 5 and later assign a = 10, Python creates a new integer object 10 and assigns it to a. The original integer 5 is discarded. This process doesn’t change the value of a; rather, a new object is created and assigned.

Mutable Types:

When you assign la = [1, 2, 3, 4] and then update la[2] = 5, it changes the third element of the list. The list la itself remains the same, but a part of its content has been modified.

Argument Passing in Python Functions:

  • Immutable Types: Similar to pass-by-value in C++, such as integers, strings, and tuples. For instance, in fun(a), the value of a is passed, and any changes within fun(a) will not affect the original a object. Instead, a new object is created.

  • Mutable Types: Similar to pass-by-reference in C++, such as lists and dictionaries. In fun(la), the actual list la is passed, and modifications within the function will affect the list outside the function as well.

In Python, everything is an object. Technically, we can’t say whether Python uses pass-by-value or pass-by-reference; instead, we talk about passing immutable objects or mutable objects.


Example: Passing Immutable Objects

Use the id() function to check how memory addresses change.

Example (Python 3.0+):

def change(a):
    print(id(a))   # Points to the same object
    a = 10
    print(id(a))   # Now points to a new object

a = 1
print(id(a))
change(a)


Output:

4379369136
4379369136
4379369424


This shows that the function parameter and the original argument point to the same object initially (same id). However, after modifying the parameter inside the function, it points to a different object.


Example: Passing Mutable Objects

If a mutable object is modified inside a function, the changes affect the original object as well.

Example (Python 3.0+):

#!/usr/bin/python3

# Function to modify a list
def changeme(mylist):
   "Modifies the passed list"
   mylist.append([1, 2, 3, 4])
   print("Values inside the function: ", mylist)
   return

# Calling changeme function
mylist = [10, 20, 30]
changeme(mylist)
print("Values outside the function: ", mylist)


Output:

Values inside the function:  [10, 20, 30, [1, 2, 3, 4]]
Values outside the function:  [10, 20, 30, [1, 2, 3, 4]]


The object passed to the function and the modified object outside the function share the same reference, leading to the same changes.


Parameters

When calling functions in Python, there are different types of formal parameters:

  1. Required Parameters

  2. Keyword Parameters

  3. Default Parameters

  4. Variable-length Parameters

Required Parameters:

Required parameters must be passed in the correct order. The number of arguments in the function call must match the function’s definition.

If you call printme() without passing an argument, it results in a syntax error:

Example (Python 3.0+):

#!/usr/bin/python3

# Function definition
def printme(str):
   "Prints any passed string"
   print(str)
   return

# Calling the function without an argument will raise an error
printme()


Output:

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    printme()
TypeError: printme() missing 1 required positional argument: 'str'



Keyword Arguments:

Keyword arguments are closely tied to function calls. Using keyword arguments allows us to pass values to functions in a way that does not depend on the order of parameters, as long as the parameter names are provided.

This example uses keyword arguments to call the printme() function:

Example (Python 3.0+):

#!/usr/bin/python3

# Function definition
def printme(str):
   "Prints any passed string"
   print(str)
   return

# Calling printme using keyword arguments
printme(str = "Runoob Tutorial")


Output:

Runoob Tutorial


Example: Parameters Without Specified Order

You don’t need to follow the order of parameters when using keyword arguments.

Example (Python 3.0+):

#!/usr/bin/python3

# Function definition
def printinfo(name, age):
   "Prints the provided information"
   print("Name: ", name)
   print("Age: ", age)
   return

# Calling printinfo with parameters out of order
printinfo(age=50, name="Runoob")


Output:

Name:  Runoob
Age:  50

Default Parameters

When calling a function, if no argument is passed, the function will use the default parameter. In the following example, if the age parameter is not provided, the function will use the default value:

Example (Python 3.0+):

#!/usr/bin/python3
 
# Function definition
def printinfo(name, age=35):
   "Prints any passed information"
   print("Name: ", name)
   print("Age: ", age)
   return
 
# Calling the printinfo function
printinfo(age=50, name="runoob")
print("------------------------")
printinfo(name="runoob")


Output:

Name:  runoob
Age:  50
------------------------
Name:  runoob
Age:  35



Variable-length Arguments

Sometimes, you may need a function that can handle more arguments than you initially declared. These are called variable-length arguments, and unlike the previous two types, they are not named in the function definition. The basic syntax is as follows:

def functionname([formal_args,] *var_args_tuple):
   "Function docstring"
   function_suite
   return [expression]

The parameter prefixed with an asterisk * will be a tuple, containing all the unnamed variable arguments.

Example (Python 3.0+):

#!/usr/bin/python3

# Function definition
def printinfo(arg1, *vartuple):
   "Prints any passed arguments"
   print("Output: ")
   print(arg1)
   print(vartuple)
 
# Calling the printinfo function
printinfo(70, 60, 50)


Output:

Output: 
70
(60, 50)


If no additional arguments are passed during the function call, the variable is an empty tuple. You can also choose not to pass any unnamed arguments to the function. Here’s an example:

Example (Python 3.0+):

#!/usr/bin/python3
 
# Function definition
def printinfo(arg1, *vartuple):
   "Prints any passed arguments"
   print("Output: ")
   print(arg1)
   for var in vartuple:
      print(var)
   return
 
# Calling the printinfo function
printinfo(10)
printinfo(70, 60, 50)


Output:

Output:
10
Output:
70
60
50



Double Asterisk ** in Function Parameters

You can also pass a variable-length argument dictionary using two asterisks **. The basic syntax is:

def functionname([formal_args,] **var_args_dict):
   "Function docstring"
   function_suite
   return [expression]

Arguments prefixed with two asterisks ** are treated as a dictionary of key-value pairs.

Example (Python 3.0+):

#!/usr/bin/python3

# Function definition
def printinfo(arg1, **vardict):
   "Prints any passed arguments"
   print("Output: ")
   print(arg1)
   print(vardict)
 
# Calling the printinfo function
printinfo(1, a=2, b=3)


Output:

Output: 
1
{'a': 2, 'b': 3}



Single Asterisk * in Function Parameters

When a single asterisk * appears in a function’s parameter list, the parameters following the * must be passed as keyword arguments.

Example:

def f(a, b, *, c):
    return a + b + c


When calling this function, the argument for c must be passed using a keyword, as shown below:

>>> f(1, 2, 3)   # Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given

>>> f(1, 2, c=3) # Correct
6

Anonymous Functions

In Python, anonymous functions are created using the lambda keyword.

An anonymous function means that it is defined without using the def keyword. Instead, lambda expressions are used, which are much simpler than standard function definitions. The body of a lambda function consists of a single expression, not a block of code. It can encapsulate limited logic within the expression itself.

  • A lambda function has its own namespace and can only access parameters passed to it or variables in its local scope.

  • Although lambda functions are written in one line, they are not the same as C or C++ inline functions, which are designed to avoid stack memory usage and improve execution speed for small functions.

Syntax

The syntax of a lambda function consists of a single statement:

lambda [arg1 [, arg2, ... argn]]: expression

Here's an example that adds 10 to the argument a:

Example:

x = lambda a : a + 10
print(x(5))


Output:

15


Here’s another example of a lambda function that takes two parameters:

Example (Python 3.0+):

#!/usr/bin/python3

# Defining a lambda function
sum = lambda arg1, arg2: arg1 + arg2

# Calling the sum function
print("The sum is: ", sum(10, 20))
print("The sum is: ", sum(20, 20))


Output:

The sum is: 30
The sum is: 40


Encapsulating a Lambda in Another Function

You can encapsulate a lambda function inside another function to create multiple anonymous functions with similar logic. Here’s an example where a lambda function is wrapped inside myfunc, and different instances are created by passing different arguments.

Example:

def myfunc(n):
  return lambda a : a * n

mydoubler = myfunc(2)
mytripler = myfunc(3)

print(mydoubler(11))
print(mytripler(11))


Output:

22
33

For more on anonymous functions, you can refer to: Python Lambda Functions.


return Statement

The return [expression] statement is used to exit a function and optionally pass an expression back to the caller. A return statement without any arguments returns None. In previous examples, we didn’t show how to return values, so here’s an example illustrating the use of return.

Example (Python 3.0+):

#!/usr/bin/python3

# Function definition
def sum(arg1, arg2):
   "Returns the sum of two arguments."
   total = arg1 + arg2
   print("Inside the function: ", total)
   return total

# Calling the sum function
total = sum(10, 20)
print("Outside the function: ", total)


Output:

Inside the function: 30
Outside the function: 30

Positional-only Parameters

Starting with Python 3.8, a new syntax / was introduced to specify that certain function parameters must be passed as positional arguments and cannot be passed as keyword arguments.

In the example below, the parameters a and b must be positional, while c and d can be either positional or keyword arguments, and e and f must be keyword arguments:

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

The following method calls are valid:

f(10, 20, 30, d=40, e=50, f=60)

But the following calls will raise an error:

f(10, b=20, c=30, d=40, e=50, f=60)   # 'b' cannot be used as a keyword argument
f(10, 20, 30, 40, 50, f=60)           # 'e' must be passed as a keyword argument