Python Functions, Modules & Packages
Python Functions, Modules & Packages
P a g e 1 | 45
Function Name Description
Python bool() Return or convert a value to a Boolean value i.e., True or False
Python dir() Returns list of the attributes and methods of any object
Takes two numbers and returns a pair of numbers consisting of their
Python divmod()
quotient and remainder
Adds a counter to an iterable and returns it in a form of enumerating
Python enumerate()
object
Parses the expression passed to it and runs python expression(code)
Python eval()
within the program
Python exec() Used for the dynamic execution of the program
Filters the given sequence with the help of a function that tests each
Python filter()
element in the sequence to be true or not
Python float() Return a floating-point number from a number or a string
P a g e 2 | 45
Python format() Formats a specified value
Python locals() Returns the dictionary of the current local symbol table
Returns a map object(which is an iterator) of the results after applying
Python map()
the given function to each item of a given iterable
Returns the largest item in an iterable or the largest of two or more
Python max()
arguments
Python memoryview() Returns memory view of an argument
Returns the smallest item in an iterable or the smallest of two or more
Python min()
arguments
Python next() Receives the next item from the iterator
P a g e 3 | 45
Python oct() returns octal representation of integer in a string format.
P a g e 4 | 45
2. User-defined function.
We can create our own functions based on our requirements.
Creating a function in Python:
We can create a Python function using the def keyword.
# A simple Python function
def fun():
print("Welcome to ATESTC")
Calling a Python Function:
After creating a function we can call it by using the name of the function followed
by parenthesis containing parameters of that particular function.
# A simple Python function
def fun():
print("Welcome to ATESTC")
# Driver code to call a function
fun()
Output: Welcome to ATESTC
Arguments:
An argument is a value that is passed to a function when it is called. It might be a
variable, value or object passed to a function or method as input. They are written
when we are calling the function.
P a g e 5 | 45
def sum(a,b):
print(a+b)
# Here the values 1,2 are arguments
sum(1,2)
Outpt: 3
Default Arguments:
A default argument is a parameter that assumes a default value if a value is not
provided in the function call for that argument. The following example illustrates
Default arguments.
P a g e 6 | 45
# Python program to demonstrate default arguments
def myFun(x, y=50):
print("x: ", x)
print("y: ", y)
# Driver code (We call myFun() with only argument)
myFun(10)
Output: x: 10
y: 50
Positional Arguments:
Positional Arguments are needed to be included in proper order i.e the first
argument is always listed first when the function is called, second argument needs to be
called second and so on.
def person_name(first_name, second_name):
print(first_name+second_name)
# First name is Ram placed first
# Second name is Babu place second
person_name("Ram","Babu")
Output: RamBabu
We used the Position argument during the function call so that the first argument (or
value) is assigned to name and the second argument (or value) is assigned to age. By
changing the position, or if you forget the order of the positions, the values can be used
in the wrong places, as shown in the Case-2 example below, where 27 is assigned to the
name and Suraj is assigned to the age.
def nameAge(name, age):
print("Hi, I am", name)
print("My age is ", age)
# You will get correct output because argument is given in order
print("Case-1:")
nameAge("Suraj", 27)
# You will get incorrect output because argument is not in order
print("\nCase-2:")
nameAge(27, "Suraj")
P a g e 7 | 45
Output: Case-1:
Hi, I am Suraj
My age is 27
Case-2:
Hi, I am 27
My age is Suraj
Keyword Arguments:
Keyword Arguments is an argument passed to a function or method which is
preceded by a keyword and an equal to sign. The order of keyword argument with
respect to another keyword argument does not matter because the values are being
explicitly assigned.
def person_name(first_name, second_name):
print(first_name+second_name)
# Here we are explicitly assigning the values
person_name(second_name="Babu",first_name="Ram")
Output: RamBabu
P a g e 8 | 45
Example 1: Variable length non-keywords argument
# Python program to illustrate *args for variable number of arguments
def myFun(*argv):
for arg in argv:
print(arg)
myFun('Hello', 'Welcome', 'to', ‘Python Class’)
Output: Hello
Welcome
to
Python Class
Docstring:
The first string after the function is called the Document string or Docstring in short. This is
used to describe the functionality of the function. The use of docstring in functions is
optional but it is considered a good practice.
The below syntax can be used to print out the docstring of a function:
Syntax: print(function_name.__doc__)
Example: Adding Docstring to the function
# A simple Python function to check whether x is even or odd
def evenOdd(x):
"""Function to check if the number is even or odd"""
P a g e 9 | 45
if (x % 2 == 0):
print("even")
else:
print("odd")
# Driver code to call the function
print(evenOdd.__doc__)
Output: Function to check if the number is even or odd
Python Lambda Functions are anonymous function means that the function is without a
name. As we already know that the def keyword is used to define a normal function in
Python. Similarly, the lambda keyword is used to define an anonymous function in Python.
Python Lambda Function Syntax:
lambda arguments: expression
• This function can have any number of arguments but only one expression, which is
evaluated and returned.
• One is free to use lambda functions wherever function objects are required.
• You need to keep in your knowledge that lambda functions are syntactically
restricted to a single expression.
• It has various uses in particular fields of programming, besides other types of
expressions in functions.
P a g e 12 | 45
Example 1: Condition Checking Using Python lambda function
format_numeric = lambda num: f"{num:e}" if isinstance(num, int) else f"{num:,.2f}"
print("Int formatting:", format_numeric(1000000))
print("float formatting:", format_numeric(999999.789541235))
Output: Int formatting: 1.000000e+06
float formatting: 999,999.79
P a g e 13 | 45
20
30
40
Example 2: Python Lambda Function with if-else
# Example of lambda function using if-else
Max = lambda a, b : a if(a > b) else b
print(Max(1, 2))
Output: 2
P a g e 14 | 45
The filter() function in Python takes in a function and a list as arguments. This offers an
elegant way to filter out all the elements of a sequence “sequence”, for which the
function returns True.
filter(function, sequence)
Parameters:
function: function that tests if each element of a sequence true or not.
sequence: sequence which needs to be filtered, it can be sets, lists, tuples, or
containers of any iterators.
Returns:
returns an iterator that is already filtered.
Here is a small program that returns the odd numbers from an input list:
Example 1: Filter out all odd numbers using filter() and lambda function
Here, lambda x: (x % 2 != 0) returns True or False if x is not even. Since filter() only
keeps elements where it produces True, thus it removes all odd numbers that generated
False.
li = [5, 7, 22, 97, 54, 62, 77, 23, 73, 61]
final_list = list(filter(lambda x: (x % 2 != 0), li))
print(final_list)
Output: [5, 7, 97, 77, 23, 73, 61]
Example 2: Filter all people having age more than 18, using lambda and filter() function
# Python 3 code to people above 18 yrs
ages = [13, 90, 17, 59, 21, 60, 5]
adults = list(filter(lambda age: age > 18, ages))
print(adults)
Output: [90, 59, 21, 60]
Example 2: Transform all elements of a list to upper case using lambda and map()
function
# here we intend to change all animal names to upper case and return the same
uppered_animals = list(map(lambda animal: [Link](), animals))
print(uppered_animals)
Output: ['DOG', 'CAT', 'PARROT', 'RABBIT']
P a g e 16 | 45
Example 1: Sum of all elements in a list using lambda and reduce() function
Example 2: Find the maximum element in a list using lambda and reduce() function
In Python, a decorator is a design pattern that allows you to modify the functionality of a
function by wrapping it in another function.
The outer function is called the decorator, which takes the original function as an
argument and returns a modified version of it.
Decorators are a very powerful and useful tool in Python since it allows programmers to
modify the behaviour of a function or class. Decorators allow us to wrap another function
in order to extend the behaviour of the wrapped function, without permanently
modifying it. But before diving deep into decorators let us understand some concepts that
will come in handy in learning the decorators.
P a g e 17 | 45
First Class Objects
In Python, functions are first class objects which means that functions in Python can be
used or passed as arguments.
Properties of first class functions:
• A function is an instance of the Object type.
• You can store the function in a variable.
• You can pass the function as a parameter to another function.
• You can return the function from a function.
• You can store them in data structures such as hash tables, lists, …
P a g e 18 | 45
greeting = func("""Hi, I am created by a function passed as an argument.""")
print (greeting)
greet(shout)
greet(whisper)
Output: HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.
In the above example, the greet function takes another function as a parameter (shout and
whisper in this case). The function passed as an argument is then called inside the function
greet.
Example 3: Returning functions from another function.
# Python program to illustrate functions
# Functions can return another function
def create_adder(x):
def adder(y):
return x+y
return adder
add_15 = create_adder(15)
print(add_15(10))
Output: 25
In the above example, we have created a function inside of another function and then have
returned the function created inside.
Decorators:
As stated above the decorators are used to modify the behaviour of function or class. In
Decorators, functions are taken as the argument into another function and then called
inside the wrapper function.
Syntax for Decorator:
@atestc_decorator
def hello_decorator():
print("ATESTC")
'''Above code is equivalent to -
def hello_decorator():
print("ATESTC")
P a g e 19 | 45
hello_decorator = atestc _decorator(hello_decorator)'''
In the above code, atestc _decorator is a callable function, that will add some code on
the top of some another callable function, hello_decorator function and return the
wrapper function.
Example:
# defining a decorator
def hello_decorator(func):
# inner1 is a Wrapper function in which the argument is called
# inner function can access the outer local functions like in this case "func"
def inner1():
print("Hello, this is before function execution")
# calling the actual function now inside the wrapper function.
func()
print("This is after function execution")
return inner1
# defining a function, to be called inside wrapper
def function_to_be_used():
print("This is inside the function !!")
# passing 'function_to_be_used' inside the decorator to control its behaviour
function_to_be_used = hello_decorator(function_to_be_used)
# calling the function
function_to_be_used()
Output: Hello, this is before function execution
This is inside the function !!
This is after function execution
Let’s see the behaviour of the above code and how it runs step by step when the
“function_to_be_used” is called.
P a g e 20 | 45
P a g e 21 | 45
Another example where we can easily find out the execution time of a function using a
decorator.
# importing libraries
import time
import math
# decorator to calculate duration taken by any function.
def calculate_time(func):
# added arguments inside the inner1, if function takes any arguments,
# can be added like this.
def inner1(*args, **kwargs):
# storing time before function execution
begin = [Link]()
func(*args, **kwargs)
# storing time after function execution
end = [Link]()
print("Total time taken in : ", func.__name__, end - begin)
return inner1
# this can be added to any function present, in this case to calculate a factorial
@calculate_time
def factorial(num):
# sleep 2 seconds because it takes very less time so that you can see the actual
difference
[Link](2)
print([Link](num))
# calling the function.
factorial(10)
Output: 3628800
Total time taken in : factorial 2.0061802864074707
P a g e 22 | 45
Example:
def hello_decorator(func):
def inner1(*args, **kwargs):
print("before Execution")
# getting the returned value
returned_value = func(*args, **kwargs)
print("after Execution")
# returning the value to the original frame
return returned_value
return inner1
# adding decorator to the function
@hello_decorator
def sum_two_numbers(a, b):
print("Inside the function")
return a + b
a, b = 1, 2
# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))
Output:
before Execution
Inside the function
after Execution
Sum = 3
In the above example, you may notice a keen difference in the parameters of the inner
function. The inner function takes the argument as *args and **kwargs which means that a
tuple of positional arguments or a dictionary of keyword arguments can be passed of any
length. This makes it a general decorator that can decorate a function having any number of
arguments.
P a g e 23 | 45
In simpler terms chaining decorators means decorating a function with multiple
decorators.
P a g e 24 | 45
Python Generators:
In Python, a generator is a function that returns an iterator that produces a sequence of
values when iterated over.
Generators are useful when we want to produce a large sequence of values, but we don't
want to store all of them in memory at once.
Create Python Generator:
In Python, similar to defining a normal function, we can define a generator function using
the def keyword, but instead of the return statement we use the yield statement.
def generator_name(arg):
# statements
yield something
Here, the yield keyword is used to produce a value from the generator.
When the generator function is called, it does not execute the function body immediately.
Instead, it returns a generator object that can be iterated over to produce the values.
Example:
def my_generator(n):
# initialize counter
value = 0
# loop until counter is less than n
while value < n:
# produce the current value of the counter
yield value
# increment the counter
value += 1
# iterate over the generator object produced by my_generator
for value in my_generator(3):
# print each value produced by generator
print(value)
Output: 0
1
2
P a g e 25 | 45
In the above example, the my_generator() generator function takes an integer n as an
argument and produces a sequence of numbers from 0 to n-1.
The yield keyword is used to produce a value from the generator and pause the generator
function's execution until the next value is requested.
The for loop iterates over the generator object produced by my_generator(), and the print
statement prints each value produced by the generator.
We can also create a generator object from the generator function by calling the function
like we would any other function as,
generator = my_range(3)
print(next(generator)) # 0
print(next(generator)) # 1
print(next(generator)) # 2
Here, expression is a value that will be returned for each item in the iterable.
The generator expression creates a generator object that produces the values of
expression for each item in the iterable, one at a time, when iterated over.
Example:
Output: 0
1
4
P a g e 26 | 45
9
16
Here, we have created the generator object that will produce the squares of the
numbers 0 through 4 when iterated over.
And then, to iterate over the generator and get the values, we have used the for loop.
Use of Python Generators:
There are several reasons that make generators a powerful implementation.
1. Easy to Implement:
Generators can be implemented in a clear and concise way as compared to their
iterator class counterpart. Following is an example to implement a sequence of
power of 2 using an iterator class.
class PowTwo:
def __init__(self, max=0):
self.n = 0
[Link] = max
def __iter__(self):
return self
def __next__(self):
if self.n > [Link]:
raise StopIteration
result = 2 ** self.n
self.n += 1
return result
The above program was lengthy and confusing. Now, let's do the same using a
generator function.
def PowTwoGen(max=0):
n=0
while n < max:
yield 2 ** n
n += 1
Since generators keep track of details automatically, the implementation was concise
and much cleaner.
P a g e 27 | 45
2. Memory Efficient:
A normal function to return a sequence will create the entire sequence in memory
before returning the result. This is an overkill, if the number of items in the sequence
is very large. Generator implementation of such sequences is memory friendly and is
preferred since it only produces one item at a time.
3. Represent Infinite Stream
Generators are excellent mediums to represent an infinite stream of data. Infinite
streams cannot be stored in memory, and since generators produce only one item at
a time, they can represent an infinite stream of data.
The following generator function can generate all the even numbers (at least in
theory).
def all_even():
n=0
while True:
yield n
n += 2
4. Pipelining Generators
Multiple generators can be used to pipeline a series of operations. This is best
illustrated using an example. Suppose we have a generator that produces the
numbers in the Fibonacci series. And we have another generator for squaring
numbers. If we want to find out the sum of squares of numbers in the Fibonacci
series, we can do it in the following way by pipelining the output of generator
functions together.
def fibonacci_numbers(nums):
x, y = 0, 1
for _ in range(nums):
x, y = y, x+y
yield x
def square(nums):
for num in nums:
yield num**2
print(sum(square(fibonacci_numbers(10))))
Output: 4895
P a g e 28 | 45
2.4 Module basic usage, namespaces, reloading modules. – math, random,
datetime, etc.
Python Module:
A Python module is a file containing Python definitions and statements. A module can
define functions, classes, and variables. A module can also include runnable code.
Grouping related code into a module makes the code easier to understand and use. It also
makes the code logically organized.
Module is a file that contains code to perform a specific task. A module may contain
variables, functions, classes etc.
Create a simple Python module
Let’s create a simple [Link] in which we define two functions, one add and another
subtract.
# A simple module, [Link]
def add(x, y):
return (x+y)
def subtract(x, y):
return (x-y)
Example: [Link]
# Python Module addition
def add(a, b):
result = a + b
return result
P a g e 29 | 45
Note: This does not import the functions or classes directly instead imports the
module only. To access the functions inside the module the dot(.) operator is used.
P a g e 30 | 45
# if we simply do "import math", then
# [Link](16) and [Link]()
# are required.
print(sqrt(16))
print(factorial(6))
Output: 4.0
720
Locating Python Modules:
Whenever a module is imported in Python the interpreter looks for several
locations. First, it will check for the built-in module, if not found then it looks for a
list of directories defined in the [Link]. Python interpreter searches for the module
in the following manner –
• First, it searches for the module in the current directory.
• If the module isn’t found in the current directory, Python then searches each
directory in the shell variable PYTHONPATH. The PYTHONPATH is an
environment variable, consisting of a list of directories.
• If that also fails python checks the installation-dependent list of directories
configured at the time Python is installed.
• Here, [Link] is a built-in variable within the sys module. It contains a list of
directories that the interpreter will search for the required module.
P a g e 31 | 45
Renaming the Python module:
We can rename the module while importing it using the keyword.
Syntax: Import Module_name as Alias_name
# importing sqrt() and factorial from the
# module math
import math as mt
# if we simply do "import math", then
# [Link](16) and [Link]()
# are required.
print([Link](16))
print([Link](6))
Output: 4.0
720
The dir() built-in function:
In Python, we can use the dir() function to list all the function names in a module.
For example, earlier we have defined a function add() in the module example.
dir(example)
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__initializing__',
'__loader__',
'__name__',
'__package__',
'add']
Here, we can see a sorted list of names (along with add). All other names that
begin with an underscore are default Python attributes associated with the module
(not user-defined).
For example, the __name__ attribute contains the name of the module.
P a g e 32 | 45
import example
example.__name__
Output: example
All the names defined in our current namespace can be found out using the dir() function
without any arguments.
All the names defined in our current namespace can be found out using the dir() function
without any arguments.
a=1
b = "hello"
import math
dir()
['__builtins__', '__doc__', '__name__', 'a', 'b', 'math', 'pyscripter']
import datetime
print(dir(datetime))
Output:
['MAXYEAR', 'MINYEAR', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'date', 'datetime', 'datetime_CAPI', 'sys', 'time',
'timedelta', 'timezone', 'tzinfo']
Among all the attributes of datetime module, the most commonly used classes in the
datetime module are:
• [Link] - represents a single point in time, including a date and a time.
• [Link] - represents a date (year, month, and day) without a time.
• [Link] - represents a time (hour, minute, second, and microsecond) without
a date.
• [Link] - represents a duration, which can be used to perform
arithmetic with datetime objects.
Example : Print today's year, month and day
from datetime import date
# date object of today's date
today = [Link]()
print("Current year:", [Link])
print("Current month:", [Link])
print("Current day:", [Link])
Output: Current year: 2023
Current month: 5
Current day: 11
P a g e 37 | 45
Python [Link] Class:
A time object instantiated from the time class represents the local time.
P a g e 38 | 45
Example: Print year, month, hour, minute and timestamp
Example:
# importing built-in module math
import math
# using square root(sqrt) function contained in math module
print([Link](25))
# using pi function contained in math module
print([Link])
# 2 radians = 114.59 degrees
print([Link](2))
# 60 degrees = 1.04 radians
print([Link](60))
# Sine of 2 radians
print([Link](2))
# Cosine of 0.5 radians
print([Link](0.5))
# Tangent of 0.23 radians
print([Link](0.23))
# 1 * 2 * 3 * 4 = 24
print([Link](4))
P a g e 39 | 45
# importing built in module random
import random
# printing random integer between 0 and 5
print([Link](0, 5))
# print random floating point number between 0 and 1
print([Link]())
# random number between 0 and 100
print([Link]() * 100)
List = [1, 4, True, 800, "python", 27, "hello"]
# using choice function in random module for choosing a random element from
a set such as a list
print([Link](List))
# importing built in module datetime
import datetime
from datetime import date
import time
# Returns the number of seconds since the Unix Epoch, January 1st 1970
print([Link]())
# Converts a number of seconds to a date object
print([Link](454554))
Output: 5.0
3.141592653589793
114.59155902616465
1.0471975511965976
0.9092974268256817
0.8775825618903728
0.23414336235146527
24
3
0.45311797333062176
59.63729354765712
python
1683801210.8296685
P a g e 40 | 45
1970-01-06
Python Namespace:
To simply put it, a namespace is a collection of names. In Python, we can imagine a
namespace as a mapping of every name we have defined to corresponding objects. It is
used to store the values of variables and other objects in the program, and to associate
them with a specific name. This allows us to use the same name for different variables or
objects in different parts of your code, without causing any conflicts or confusion.
Types of Python namespace:
A namespace containing all the built-in names is created when we start the Python
interpreter and exists as long as the interpreter runs.
This is the reason that built-in functions like id(), print() etc. are always available to us
from any part of the program. Each module creates its own global namespace.
These different namespaces are isolated. Hence, the same name that may exist in different
modules does not collide.
Modules can have various functions and classes. A local namespace is created when a
function is called, which has all the names defined in it.
P a g e 41 | 45
Python Variable Scope:
Although there are various unique namespaces defined, we may not be able to access all of
them from every part of the program. The concept of scope comes into play.
A scope is the portion of a program from where a namespace can be accessed directly
without any prefix.
At any given moment, there are at least three nested scopes.
1. Scope of the current function which has local names
2. Scope of the module which has global names
3. Outermost scope which has built-in names
When a reference is made inside a function, the name is searched in the local namespace,
then in the global namespace and finally in the built-in namespace.
Example 1: Scope and Namespace in Python
P a g e 42 | 45
Here,
• global_var - is in the global namespace with value 10
• outer_val - is in the local namespace of outer_function() with value 20
• inner_val - is in the nested local namespace of inner_function() with value 30
When the code is executed, the global_var global variable is printed first, followed by the
local variable: outer_var and inner_var when the outer and inner functions are called.
P a g e 43 | 45
2.1 Package: import basics
Python modules may contain several classes, functions, variables, etc. whereas Python
packages contain several modules. In simpler terms, Package in Python is a folder that
contains various modules as files.
Creating Package
Let’s create a package in Python named mypckg that will contain two modules mod1 and
mod2. To create this module follow the below steps:
• Create a folder named mypckg.
• Inside this folder create an empty Python file i.e. __init__.py
• Then create two modules mod1 and mod2 in this folder.
[Link]
def show():
print("Welcome to ATESTC")
[Link]
return a+b
P a g e 44 | 45
Understanding __init__.py
__init__.py helps the Python interpreter recognize the folder as a package. It also specifies
the resources to be imported from the modules. If the __init__.py is empty this means that
all the functions of the modules will be imported. We can also specify the functions from
each module to be made available.
For example, we can also create the __init__.py file for the above module as:
from .mod1 import show
from .mod2 import sum
This __init__.py will only allow the show and sum functions from the mod1 and mod2
modules to be imported.
P a g e 45 | 45