Functions

  • Function is a group of related statements that perform a specific task.
  • Function help break large programs into smaller and modular chunks
  • Function makes the code more organised and easy to manage
  • Function avoids repetition and there by promotes code reusability

Two types of functions

  1. Built-In Functions
  2. User Defined Functions

Function Syntax

def function_name(arguments):
    '''This is the docstring for the function'''
    # note the indentation, anything inside the function must be indented
    # function code goes here
    ...
    return

# calling the function
function_name(arguments)

Example 1


In [1]:
# Simple Function
def greet():
    '''Simple Greet Function'''
    print('Hello World')
    
greet()


Hello World

Example 2


In [4]:
# Function with arguments
def greet(name):
    '''Simple Greet Function with arguments'''
    print('Hello ', name)
    
greet('John')


Hello  John

In [6]:
# printing the doc string
print(greet.__doc__)


Simple Greet Function with arguments

Example 3


In [9]:
# Function with return statement
def add_numbers(num1,num2):
    return num1 + num2

print(add_numbers(2,3.0))


5.0

In [11]:
# Since arguments are not strongly typed you can even pass a string
print(add_numbers('Hello','World'))


HelloWorld

Scope and Lifetime of Variables

Variables in python has local scope which means parameters and variables defined inside the function is not visible from outside.

Lifetime of a variable is how long the variable exists in the memory. Lifetime of variables defined inside the function exists as long as the function executes. They are destroyed once the function is returned.


In [14]:
def myfunc():
    x = 5
    print('Value inside the function ',x)
    
x = 10
myfunc()
print('Value outside the function',x)


Value inside the function  10
Value outside the function 10

Variables defined outside the function are visible from inside which means they have a global scope.


In [15]:
def myfunc():
    #x = 5
    print('Value inside the function ',x)
    
x = 10
myfunc()
print('Value outside the function',x)


Value inside the function  10
Value outside the function 10
Global Variable

Variables declared inside the function are not available outside. The following example will generate an error.


In [23]:
def myfunc():
    y = 5
    print('Value inside the function ',y)
    
myfunc()
print('Value outside the function',y)


Value inside the function  5
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-23-e6b4cb9d525a> in <module>()
      4 
      5 myfunc()
----> 6 print('Value outside the function',y)

NameError: name 'y' is not defined

You have to use the global keyword for variables if you want to use those variables declared outside the function inside the function


In [24]:
def myfunc():
    global z
    z = 5
    print('Value inside the function ',z)
    
#z = 10
myfunc()
print('Value outside the function',z)


Value inside the function  5
Value outside the function 5

Function Arguments

Example 1


In [25]:
def greet(name,msg):
    '''Simple greet function with name and message arguments'''
    print("Hello " + name + ', ' + msg)

greet('John','Good Morning!')


Hello John, Good Morning!

In [26]:
# this will generate error since we missed one argument
greet('John')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-26-7a2412e31c55> in <module>()
      1 # this will generate error since we missed one argument
----> 2 greet('John')

TypeError: greet() missing 1 required positional argument: 'msg'

Default Arguments

Default arguments will be used if the argument value is not passed to the function. If a value is passed then it will overwrite the default value.


In [27]:
def greet(name,msg='Good Evening!'):
    '''Simple greet function with name and message arguments'''
    print("Hello " + name + ', ' + msg)

greet('John')


Hello John, Good Evening!

One rule for default arguments is that once an argument has a default value then all the arguments to the right of it must also have default values. The following example will produce an error.


In [28]:
def greet(name,msg='Good Evening!',salute):
    '''Simple greet function with name and message arguments'''
    print("Hello " + salute + '.' + name + ', ' + msg)

greet('John','Good Evening','Mr')


  File "<ipython-input-28-c2db0cd39e7e>", line 1
    def greet(name,msg='Good Evening!',salute):
             ^
SyntaxError: non-default argument follows default argument

In [29]:
def greet(name,msg='Good Evening!',salute='Mr'):
    '''Simple greet function with name and message arguments'''
    print("Hello " + salute + '.' + name + ', ' + msg)

greet('John','Good Evening','Mrs')


Hello Mrs.John, Good Evening

Keyword Arguments

Python allows functions to be called using keyword arguments. When we call functions in this way, the order of the arguments can be changed.


In [30]:
def greet(name,msg='Good Evening!',salute='Mr'):
    '''Simple greet function with name and message arguments'''
    print("Hello " + salute + '.' + name + ', ' + msg)

In [31]:
# keyword arguments
greet(name="Jack",msg="How are you?")


Hello Mr.Jack, How are you?

In [33]:
# keyword arguments - out of order
greet(msg='How do you do?',name="Brian")


Hello Mr.Brian, How do you do?

In [35]:
# mix of keyword and positional arguments
greet("Jill",salute='Ms',msg="Good to see you.")


Hello Ms.Jill, Good to see you.

However please note that having a positional argument after keyword argument will result into errors. For example the following example will generate an error.


In [36]:
greet(name="Keith","Good Afternoon")


  File "<ipython-input-36-052fd2bc6ad8>", line 1
    greet(name="Keith","Good Afternoon")
                      ^
SyntaxError: positional argument follows keyword argument

Arbitrary Arguments

Python allows unknown number of arguments to be passed to a function through arbitrary arguments. Arbitrary arguments are declared by placing an asterix (*) in front of the parameter name


In [37]:
def greet(*names):
    '''This function greets all with a Hello'''
    for name in names:
        print('Hello ',name)
    
greet('John','Keith','Brian','Jose')


Hello  John
Hello  Keith
Hello  Brian
Hello  Jose

Recursive Functions

Function that calls itself is called recursive function.

While the recursive functions are clean and elegant care must be taken as it might take up lot of memory and time and it is also hard to debug.

Please note the recursive function must also have a base condition that stops the recursion otherwise the function calls itself indefinitely.


In [49]:
def factorial(num):
    if(num <= 0):
        return 0
    elif(num == 1):
        return 1
    else:
        return(num * factorial(num-1))
    
num = 4
print("Factorial of number ",num," is ",factorial(num))


Factorial of number  4  is  24

Using *args and ***kwargs in functions

When programming, you may not be aware of all the possible use cases of your code, and may want to offer more options for future programmers working with the module, or for users interacting with the code. We can pass a variable number of arguments to a function by using *args and **kwargs in our code.

Using *args


In [50]:
def multiply(x,y):
    return x * y

multiply(2,4)


Out[50]:
8

Later if we decide to extend the multiply function to accept n number of arguments, we need to use the *args feature


In [67]:
def multiply(*args):
    x = 1
    for num in args:
        x *= num
    return(x)
    
print(multiply(2,4))
print(multiply(2,4,5))
print(multiply(3,5,8,9,10))


8
40
10800

Using **kwargs

The double asterisk form of **kwargs is used to pass a keyworded, variable-length argument dictionary to a function. Again, the two asterisks (**) are the important element here, as the word kwargs is conventionally used, though not enforced by the language.


In [68]:
def print_values(**kwargs):
    for key, value in kwargs.items():
        print("The value of {} is {}".format(key, value))

print_values(
            name_1="Alex",
            name_2="Gray",
            name_3="Harper",
            name_4="Phoenix",
            name_5="Remy",
            name_6="Val"
        )


The value of name_1 is Alex
The value of name_2 is Gray
The value of name_3 is Harper
The value of name_4 is Phoenix
The value of name_5 is Remy
The value of name_6 is Val

Ordering Arguments

When ordering arguments within a function or function call, arguments need to occur in a particular order:

  • Formal positional arguments
  • *args
  • Keyword arguments
  • **kwargs

In [69]:
def some_args(arg_1, arg_2, arg_3):
    print("arg_1:", arg_1)
    print("arg_2:", arg_2)
    print("arg_3:", arg_3)

args = ("Sammy", "Casey", "Alex")
some_args(*args)


arg_1: Sammy
arg_2: Casey
arg_3: Alex

In [70]:
def some_args(arg_1, arg_2, arg_3):
    print("arg_1:", arg_1)
    print("arg_2:", arg_2)
    print("arg_3:", arg_3)

my_list = [2, 3]
some_args(1, *my_list)


arg_1: 1
arg_2: 2
arg_3: 3

In [71]:
def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
    print("kwarg_1:", kwarg_1)
    print("kwarg_2:", kwarg_2)
    print("kwarg_3:", kwarg_3)

kwargs = {"kwarg_1": "Val", "kwarg_2": "Harper", "kwarg_3": "Remy"}
some_kwargs(**kwargs)


kwarg_1: Val
kwarg_2: Harper
kwarg_3: Remy