Functions

A function is a block of code consisting of one or more Python statements which are invoked together as a sequence of instructions when the function is called.

If a function is defined with parameters then you have to invoke such a function with necessary arguments.

We have already called several functions in this book. E.g.;

print("hello")
input("please type in anything")

Both these functions are taking one argument each; 'hello' and 'please type in anything' respectively in these examples.

These functions are part of the Python programming language, and we simply used them.
Now we will create our own function a.k.a. user defined functions.

User Defined Function A function that a programmer creates is called 'User Defined Function'. This is in contrast to the builit-in functions that are released with the programming language and the user just uses it in their application. Let us take a look at a few examples of user defined functions.

Function Definition With No Arguments

Here is a function named 'print_greetings' that takes no arguments and also does not return anything:


def print_greetings():
    print('Hi there')

The above set of statements defines a function that prints out 'Hi there' everytime it is invoked. You invoke the above function as shown below

print_greetings()

You define a function by using the def keyword and the name of the function, optional parameter list and a colon, followed by the full implementation of the function. You invoke the function by using the function name following by a pair of parenthesis that contains optional arguments.

Function Definition With Arguments and Return Statement

Next let us see an example of a function definition that takes arguments and also returns something back when invoked. Here is an example of a user defined function that calculates the area of a triangle. It is named, calculate_triangle_area and it receives two input parameters; the base and the height.

Here is the full implementation of the function definition:


# function definition begins
def calculate_triangle_area(base, height):  
    area = (base * height) / 2
    return area

By now you are familiar with the statement immediately after the colon.
The only other keyword that you have not seen so far is the return.

Once this function is defined, this function can be invoked with different values for base and height. Here is an example invocation with values of 10 and 12 for base and height

calculate_triangle_area(10, 12)

The value '10' and '12' that are passed is here are the 'arguments' for the function parameters 'base' and 'height' respectively.

return keyword: You use the return keyword to pass something from the function back to the calling statement.

Let us take an example;

The calculate_triangle_area function, when invoked with argument 10 and 12,
will return a value of 10 * 12 / 2 = 60

The full code for invoking this function is shown below:


area = calculate_triangle_area(10, 12)
print(area)

When the program of execution hits a return statement, the program exits the function call and any other statements after the return is not executed.

Once a function is defined, you can invoke that function any number of times with different arguments and each time it returns the area based on the input numbers.

Note that this function is defined to receive two arguments. If you do not send the required argument within the parenthesis, then you get a TypeError

TypeError: calculate_triangle_area() missing 2 required positional argument: 'height' and 'base'

Function with no return

If a function does not return anything, then the variable that tries to receive the returned value will have a None value. Here is an example of such an invocation:

def display_greetings(name):
    print("Greetings!", name + ",",  "hope you are having a good day")

a = display_greetings("Foo")
type(a)

Output:

Greetings! Foo Hope you are having a good day
NoneType

While the above was a user defined function, you have been using such functions which return nothing. Here is an example of built-in function 'print' which return nothing:

a = print('Z')
print(a)

Output:

Z
None

Documentation

You can add documentation to a function so that others can understand how to use the function, by adding comment lines with three single or double quotes. Here is the same modified function with documentation


def calculate_triangle_area(height, base):  
    '''
    This method calculates the area of a triangle by receiving the height and base of the triangle
    as the required arguments
    '''
    area = (height * base) / 2
    return area

You notice that this function has documentations described between three single quotes. This comment line will be displayed when the user invokes 'help', another built-in function, function by sending in the name of the function as an argument.

Here is how it is invoked:

help(calculate_triangle_area)

Output:

Help on function calculatetrianglearea in module __main\:
calculate_triangle_area(height, base)
  This method calculates the area of a triangle by receiving the height and base of the triangle as the required arguments

Difference between argument and parameter

Parameters are variable names that are defined in a function. Arguments are the values that are sent to the variable defined in the function when the function is invoked.

Why Functions?

  • Repeating blocks of code should be pulled into a function to make your code modular and easy to understand
  • Most commonly used functionality can be made a function that can be imported into other programs
  • It is easy to debug a complex program if it is broken down into function as you can test each individual function easily.

Function Definitions With Default Values

Sometimes you may have to define a function with one or more default argument values. In such cases all the default arguments can be defined after all the non-default arguments are defined. Here is an example;

def do_something(state, county, country="US"):
    print(country, state, county)

do_something('MI', 'Wayne')
do_something('MI', 'Wayne', "United States of America")

output:

US MI Wayne United States of America MI Wayne

With the above function definition, you can invoke the function with or without a value for 'country'. If you pass country value then the passed in value would be considered. If it is missing then the default value defined in the function definition will be used.

Function Definitions with Variable Arguments and Keyword Arguments

Occasionally you may require a function which can accept variable arguments. In such cases you can use the * (asterisk) and/or ** (double asterisk) against the parameter names to receive many comma separated arguments and/or keyword arguments respectively. Here is an example;

def do_something(*args, **kwargs):
    print(kwargs)  # receives a dictionary of keyword arguments
    print(args)   # receives a tuple of all arguments.

do_something(1, 2, 3, country='US', county='Wayne')

output:

{'country': 'US', 'county': 'Wayne'} (1, 2, 3)

Referencing variable from outer scope inside a function

Supposing you want to reference a variable outside of the function, you can do so only by using global keyword. Here is an example

x = 10
def do_something():
    x += 100
    print(x)

do_something()

Output:

UnboundLocalError: local variable 'x' referenced before assignment

The above reference of the outer variable throws an error. You can fix this by assigning the global keyword for the variable x. Here is the code:


x = 10
def do_something():
  global x
  x = x + 100
  print(x)

do_something()

Output:
110

Note: All variables that are declared inside a function are considered local by default unless 'global' keyword is used.

However you can reference an outer variable inside the function. Here is the example to illustrate that:


x = 10
def do_something():
  print(x)

do_something()

Output:

10

Understanding variable scope in Python

By default all variables declared in a python file (the .py file which becomes a module if used by other programs) is visible to conditional code blocks like if, while, functions etc.. inside that file.

However the variables declared in a module (file) are not automatically global and hence cannot be referenced by its name alone, inside other modules (files), unlike in languages such as JavaScript. Variables which are in one module can be accessed in other modules by way of importing that module and prepending the variable name with module name/namespace.

* for Unpacking Collections

Asterisk (*) performs argument unpacking. This is used with an enclosing function. Here is an example;


def multiply(a, b):
    return a * b

print(multiply(*[2, 3]))

output:

6

Note however that, you cannot use asterisk by itself without an enclosing function. Although the example shows a list, you can replace the list with any collection object like tuple, set etc..

** for Unpacking Dictionary

You can use double asterisk (**) to unpack a dictionary into name, value pairs. Here too it should be used as an argument to a function. Here is an example;

def do_something(country,state,county):
    print(country, state, county)

my_place = {'state':'Michigan', 
                'county':'Wayne', 'country':'United States',}
do_something(**my_place)

output:

United States Michigan Wayne

 

Highlights

  • def keyword is used to define a function
  • function should be defined first before it is invoked.
  • A function is a block of code which can be called by the same or different program
  • A function can take 0 or more arguments as long as the respective parameters are defined in the function definition.
  • You can set default values to certain arguments by defining the default argument values after all the non-default arguments are defined.
  • You can also send variable length of arguments to a function by using an asterisk before the argument name.
  • You can also send a variable length of keyword arguments by using double asterisk before the keyword arguments name.
  • Multiple arguments are separated by a comma in both the definition and invocation. The order of arguments is important when you call the function- you should maintain the same order as the definition, while passing the values to the arguments unless when the function is defined for arbitrary arguments and/or keyword arguments list.
  • A return statement, if present will return any object back from the function to the calling program.
  • Function definition should come before the function is called otherwise you will get NameError

 

Naming convention

  • It is a recommended practice to name all variables with a noun and all function names should be a verb. The reasoning behind this is, a variable holds a value and inherently does not do anything else with it so it should be a noun. A function on the other had is doing something on the arguments passed. It is taking some action on it and hence should be named with a verb.

Anonymous functions a.k.a. Lambda functions

In python you can create functions without the def keyword and a name. These are called Lambda functions. Lambda functions can be created when the function has only one expression in its body. Let us take an example:


def add(x, y):
    return x + y

In the above function definition, there is only one expression x+y which is returned. So this is a good candidate for defining it as a Lambda function instead. Here is how it is defined as a Lambda:


(lambda x, y : x+y)(5, 10)

Output:
15

In this the Lambda function is taking x and y as arguments and the expression x+y after colon, is the body of the lambda function. As you can see lambda function has no name and it gets called when you pass arguments (5,10) to it through a pair of parenthesis.

Yield keyword in functions

When a function executes a return statement, it terminates the function call and returns the program of execution to the calling statement and the state of the variables inside the function is released for garbage collection. Garbage collection is a program which evicts all the de-referenced variables from memory thereby making space in memory for other relevant variables that the program generates.

If instead a function executes a yield statement then the state of the variables inside the function is saved and the function returns an object of type generator with the saved state. Here is an example:


def my_func(num):
  for i in range(10):
    num += 1
    yield num

type(my_func(10))

This generator object is iterable in any loop and the variable that is used in the yield statement is given one at a time in the loop iteration.


def my_func(num):
  for i in range(10):
    num += 1
    yield num

for i in my_func(5):
  print(i)

This prints out the value 6 through 15 which is the value of the num variable computed inside the function and returned through the yield statement

Generator expressions

Generator Expressions looks similar to list comprehensions, but does not construct a list object but constructs a generator.

Instead of creating a list and keeping the whole sequence in the memory, the generator object generates the next sequence in an iteration one at a time.


[i for i in range(5)]  # a list object with values from 1 to 5 is returned
(i for i in range(5))  # a generator object is returned that provides values from 1 to 5 during iteration

You can use the generator in any situation where a collection is required. For e.g., if you want to use the built-in sum function to find the sum of all numbers in a list, you could use either the list object or pass in the generator object. For e.g,:


sum([1, 2, 3])
sum([i for i in range(4)])
sum(i for i in range(4))

All the above get you the same result except in the first and second case, you have the entire list object in memory but in the third case, you have only the generator object in memory which produces the next sequence needed in the iteration, on demand.

pass statement

Sometimes you do have the complete implementation for a compound statement in which case you can use a pass statement which is just a placeholder so that your code compiles but does nothing in that complex statement.

For e.g.;


if True:
    pass

Remember however, the program of execution continues after the pass statement is executed if there are more statements after it. This is not like return statement. pass just results in no operation (NOP). Here the below statement does print 'hi'


if True:
    pass
    print('hi)

We typically use pass when the implementation of a block of code whether in a function, conditionals, loops etc., is deferred to a later date.

results matching ""

    No results matching ""