Python Module 4
Python Module 4
DEPARTMENT OF ECE
NOTES
PYTHON PROGRAMMING
1BPLC105B/205B
PYTHON PROGRAMMING
Module-4
Python Programming Dept. ECE
Modules
A module is a file containing Python definitions and statements intended for use in other Python
programs. There are many Python modules that come with Python as part of the standard library.
For example: math, random and time modules
The help system contains a listing of all the standard modules that are available with Python.
Random Numbers
We often want to use random numbers in programs, here are a few typical uses:
• To play a game of chance where the computer needs to throw some dice, pick a number, or flip
a coin,
• To shuffle a deck of playing cards randomly,
• To allow/make an enemy spaceship appear at a random location and start shooting at the player,
• To simulate possible rainfall when we make a computerized model for estimating the
environmental impact of building a dam
Generating random numbers in Python is primarily done using the built-in random module. This
module provides a wide array of functions for various random number generation and random
selection tasks.
The random module is a pseudo-random number generator (PRNG). This means the numbers
it generates aren't truly random but are calculated using a deterministic algorithm. However, for
most computer science applications, including simulations, games, and security (non-
cryptographic), they are sufficiently random.
To use any function from the module, you must first import it:
import random
Python Programming Dept. ECE
1. [Link]()
Purpose: Returns a random floating-point number.
Range: - The number is greater than or equal to and strictly less than .
Example:
float_val = [Link]()
2. [Link](a, b)
Purpose: Returns a random integer.
Range: - The number is greater than or equal to and less than or equal to (inclusive on both
ends).
Note: The arguments and must be integers.
Example:
dice_roll = [Link](1, 6)
3. [Link](a, b)
Purpose: Returns a random floating-point number .
Example:
Note: Similar to the built-in range() function. The step argument is optional.
Example:
1. [Link](sequence)
Purpose: Returns a random single element from a non-empty sequence.
Example:
rand_color = [Link](colors)
2. [Link](sequence, k=n)
Purpose: Returns a list of elements randomly chosen from a sequence with replacement
(an item can be chosen multiple times).
Example:
3. [Link](sequence, k=n)
Purpose: Returns a list of elements randomly chosen from a sequence without
replacement (each item is unique). cannot be larger than the sequence length.
Example:
4. [Link](x)
Purpose: Shuffles (randomly reorders) the items of a list x in place. It modifies the
original list and returns None.
Example:
my_list = [1, 2, 3, 4, 5]
[Link](my_list)
Time Module
The time module in Python provides essential functions for working with time, including
measuring performance, pausing execution, and formatting dates and times. It's crucial for
understanding execution speed and scheduling events in programs.
Use: This is the preferred function for measuring the duration of short operations. Its
value is purely for difference calculation and isn't tied to the calendar date.
Example (Benchmarking):
Python Programming Dept. ECE
import time
start = time.perf_counter()
# Code to measure...
end = time.perf_counter()
Reference: This number is the count of seconds that have passed since the Epoch (January
1, 1970, 00:00:00 UTC).
Use: Useful for storing timestamps, comparing large time intervals, or generating unique
IDs based on time.
Use: Essential for simulations, slowing down code for visual output, or rate-limiting
interactions (e.g., waiting between sending requests to a server).
print("Starting...")
print("...Resumed!")
1 tm_mon Month 1 to 12
3 tm_hour Hour 0 to 23
Seconds
[Link](secs) Time Tuple Convert Epoch seconds to local time tuple.
(float)
1. [Link](secs)
Purpose: Converts time in seconds to a simple, fixed-format string representing the local
time.
Example:
print([Link]())
now_tuple = [Link]()
# Using format codes
Math Module
The math module provides access to mathematical functions and constants for advanced
operations beyond basic arithmetic. It is essential for engineering and complex computing tasks.
# [Link](x) - e raised to x
print(f"e^3: {[Link](3)}")
print("-" * 30)
# [Link](n) - Calculate n!
print(f"Factorial of 6 (6!): {[Link](6)}")
import seqtools
seqtools.remove_at(4, s)
print(s)
Output: ‘A sting!’
We do not include the .py file extension when importing. Python expects the file names of Python
modules to end in .py, so the file extension is not included in the import statement.
The use of modules makes it possible to break up very large programs into manageable sized parts,
and to keep related parts together.
NameSpaces
A namespace is essentially a system that maps names to objects.
Key Purpose: Preventing Conflicts The main job of namespaces is to ensure that two identical
names do not accidentally refer to the same object when they shouldn't.
If you define a function named calculate() in one module (global namespace A), and a
library you import defines its own calculate() function (global namespace B), Python uses
the module's namespace to keep them separate.
If you are inside a function (Local Namespace) and use a variable i, it won't conflict with
a separate variable i used in the main part of your script (Global Namespace).
Python Programming Dept. ECE
def change_x_locally():
# LOCAL NAMESPACE: A specific room (Private scope)
# This 'x' is a NEW, local variable inside the function.
# It does NOT change the global 'x' defined above.
x=5
print(f"Inside function (Local x): {x}")
Output:
1. Before calling function (Global x): 10
Inside function (Local x): 5
2. After calling function (Global x): 10
# Key Takeaway: The global 'x' stayed 10, proving the 'x' inside the function was separate.
• Local scope refers to identifiers declared within a function. These identifiers are kept in the
namespace that belongs to the function, and each function has its own namespace.
• Global scope refers to all the identifiers declared within the current module, or file.
Python Programming Dept. ECE
• Built-in scope refers to all the identifiers built into Python — those like range and min that can
be used without having to import anything, and are (almost) always available.
Python can help you by telling you what is in which scope. Use the functions locals, globals, and
dir to see for yourself!
Python (like most other computer languages) uses precedence rules: the same name could occur
in more than one of these scopes, but the innermost, or local scope, will always take precedence
over the global scope, and the global scope always gets used in preference to the built-in scope.
Let’s start with a simple example:
def range(n):
return 123*n
print(range(10))
What gets printed? We’ve defined our own function called range, so there is now a potential
ambiguity. When we use range, do we mean our own one, or the built-in one? Using the scope
lookup rules determines this: our own range function, not the built-in one, is called, because our
function range is in the global namespace, which takes precedence over the built-in names.
So although names likes range and min are built-in, they can be “hidden” from your use if you
choose to define your own variables or functions that reuse those names. (It is a confusing practice
to redefine built-in names — so to be a good programmer you need to understand the scope rules
and understand that you can do nasty things that will cause confusion, and then you avoid doing
them!)
n = 10
m=3
def f(n):
m=7
return 2*n+m
print(f(5), n, m)
This prints 17 10 3. The reason is that the two variables m and n in lines 1 and 2 are outside the
function in the global namespace. Inside the function, new variables called n and m are created
just for the duration of the execution of f. These are created in the local namespace of function f.
Within the body of f, the scope lookup rules determine that we use the local variables m and n. By
contrast, after we’ve returned from f, the n and m arguments to the print function refer to the
original variables on lines 1 and 2, and these have not been changed in any way by executing
function f.
Notice too that the def puts name f into the global namespace here. So it can be called on line 7.
Python Programming Dept. ECE
What is the scope of the variable n on line 1? Its scope — the region in which it is visible — is
lines 1, 2, 6, 7. It is hidden from view in lines 3, 4, 5 because of the local variable n.
When we use a dotted name, we often refer to it as a fully qualified name, because we’re saying
exactly which attribute we mean.
import math
x = [Link](10)
Here just the single identifier math is added to the current namespace. If you want to access one
of the functions in the module, you need to use the dot notation to get to it.
Here is a different arrangement:
x = sqrt(10)
The names are added directly to the current namespace, and can be used without qualification. The
name math is not itself imported, so trying to use the qualified form [Link] would give an error.
Then we have a convenient shorthand:
Of these three, the first method is generally preferred, even though it means a little more typing
each time. Although, we can make things shorter by importing a module under a different name:
import math as m
[Link]
#Output: 3.141592653589793
def area(radius):
import math
Here we imported math, but we imported it into the local namespace of area. So the name is usable
within the function body, but not in the enclosing script, because it is not in the global namespace.
my_list = [2, 4, 5, 3, 6, 1]
my_list[0] = 9
my_list
Output: [9, 4, 5, 3, 6, 1]
Tuples and strings are examples of immutable datatypes, their contents cannot be changed after
they have been created:
my_tuple = (2, 5, 3, 1)
my_tuple[0] = 9
Output :
list_one = [1, 2, 3, 4, 6]
list_two = list_one
list_two[-1] = 5
list_one
Output:
[1, 2, 3, 4, 5]
This happens, because both list_one and list_two refer to the same memory address containing the
actual list. You can check this using the built-in function id:
list_one = [1, 2, 3, 4, 6]
list_two = list_one
id(list_one) == id(list_two)
Output:
True
list_one = [1, 2, 3, 4, 6]
list_two = list_one[:]
check_id=(id(list_one) == id(list_two) )
check_id
list_two[-1] = 5
list_two
list_one
Python Programming Dept. ECE
Output
False
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 6]
Object-oriented programming
Python is an object-oriented programming language, which means that it provides features that
support object-oriented programming (OOP
Up to now, most of the programs we have been writing use a procedural programming paradigm.
In procedural programming the focus is on writing functions or procedures which operate on data.
In object-oriented programming the focus is on the creation of objects which contain both data
and functionality together.
Usually, each object definition corresponds to some object or concept in the real world, and the
functions that operate on that object correspond to the ways real-world objects interact.
class Robot:
# Attributes (Characteristics)
# These define the state (data) of the object
Python Programming Dept. ECE
color = "Silver"
# Methods (Behaviors/Actions)
print("Processing data...")
Analogy: An object is the actual physical house built from the plans. You can build many
houses (objects) from the same single blueprint (class).
robot_alpha = Robot()
robot_beta = Robot() # Changing an attribute on one object does not affect the other
robot_alpha.color = "Red"
robot_beta.color = "Blue"
Key Terminology
Python Programming Dept. ECE
class Student:
# The Constructor Method
# It takes starting parameters (name, id) and assigns them to the object using 'self'.
def init (self, name, student_id):
self.student_id = student_id
def get_greeting(self):
# The method uses 'self' to access the unique data of the specific object.
return f"Hello, I am {[Link]} (ID: {self.student_id})."
print(s_engineer.get_greeting())
print(s_science.get_greeting())
Output:
Attributes
An Attribute is simply a data variable that holds information (the state) associated with a Class or
an Object. They define the characteristics of the entity they belong to.
These are the attributes that are unique to each specific object (instance) created from the class.
Analogy: If the class is the blueprint for a cell phone, the instance attributes are the phone's
serial number, its current battery percentage, and the color it was painted.
Definition: Instance attributes are almost always defined and initialized inside the special
constructor method, init , using the self keyword.
Access: Accessed using the specific object name followed by the dot operator (e.g.,
[Link]).
class Phone:
# 1. Defined in init using 'self'
These are attributes that are shared equally by all objects created from the class. They hold data
that is common to the entire class type.
Analogy: For the cell phone class, the class attribute might be manufacturer = "TechCorp" or
default_screen_size = 6.5. Every phone object shares this same value.
Definition: Defined directly inside the class body, but outside any method.
Access: Accessed either through the Class itself ([Link]) or via an instance
([Link]).
class Phone:
# 1. Defined outside of init
manufacturer = "GlobalTech" # Class Attribute
default_os = "PyOS 10" # Class Attribute
# Accessing Class Attributes via instances (they share the same value)
print(f"{phone_x.model} OS: {phone_x.default_os}")
print(f"{phone_y.model} Manufacturer: {phone_y.manufacturer}")
Summary of Differences
class Device:
# Constructor: initializes the unique state of the object
def init (self, serial_number):
# The method uses 'self' to access and change the instance attribute 'is_on'
self.is_on = not self.is_on
Reference to the Instance: self is a required convention (it's short for "self-reference")
that refers to the specific object (instance) that called the method.
Access to Attributes: It provides the method with access to the object's unique data. You
must use self. before any attribute or other method you want to access within the class
(e.g., self.serial_number).
Python Programming Dept. ECE
device1 = Device("A001")
device2 = Device("B002")
This is fundamental for building systems where objects interact with each other.
Imagine you have two classes: Robot and Charger. A robot needs to be charged by the charger.
The charging function must know which robot it is supposed to charge. The way to tell the function
is by passing the specific Robot object to the Charger's method.
Example :
class Battery:
[Link] = charge_level
def report_charge(self):
def check_power(device_battery):
"""
"""
print("--- Power Check ---")
In the main program, we create an instance of the Battery class and then pass that instance directly
into the generic check_power function.
car_battery = Battery(charge_level=95)
drone_battery = Battery(charge_level=15)
check_power(car_battery)
Output:
Checking Car Battery:
Battery is at 15%.
This concept is often used when one object needs to perform an action on another object.
class RepairShop:
"""
Takes another object (item_to_fix) as a parameter.
"""
print(f"\nRepairing device...")
# Interaction
shop = RepairShop()
# The RepairShop object (shop) calls its method, passing the drone_battery object
# as an argument.
shop.repair_device(drone_battery)
# Check the state of the drone_battery object after the method ran
print(drone_battery.report_charge()) # Output: Battery is at 100%.
If you don't define a custom string method, Python defaults to showing the object's type and its
memory location, which is usually not helpful to a user.
class Point:
self.x = x
self.y = y
p1 = Point(5, 10)
print(p1)
Purpose: It's primarily used for debugging, logging, and inspection by developers. It
should be unambiguous and, if possible, look like valid Python code that could recreate the
object.
Rule of Thumb: Always include all key attributes in your repr output.
class Point:
p1 = Point(5, 10)
print(repr(p1))
# Output (Developer-friendly): Point(x=5, y=10)
Rule of Thumb: Be concise, elegant, and focus on the most important information.
class Point:
print(object) Python looks for str . If str is not defined, it falls back to repr .
A Factory Method is a function or class method whose sole job is to construct and return a new
instance of a class.
Analogy: Imagine a special button on a 3D Printer. You press the "Print Cube" button, and
the printer (the method) constructs a new physical Cube object and hands it to you (returns
it).
Use Case: You use this when you need a standardized way to create objects with specific,
common initial settings.
class Sensor:
[Link] = sensitivity
self.power_mode = power_mode
# Factory Method: A function that CREATES and returns a new Sensor object
@staticmethod
def create_low_power_sensor():
return new_sensor
# Usage:
# The variable 's_low' now holds a reference to a fully initialized Sensor object.
Python Programming Dept. ECE
s_low = Sensor.create_low_power_sensor()
print(s_low)
Method chaining is a powerful technique where methods are designed to modify the object's state
and then immediately return the object itself (return self). This allows you to call multiple methods
on the same object in a single, clean line.
Use Case: Common in configuration or setup steps where you need to perform sequential
actions on one specific object.
Example: Configuring a Logging System
class LoggerConfig:
def init (self):
[Link] = "INFO"
[Link] = "Console"
def show_config(self):
print(f"Logger: {[Link]} -> {[Link]}")
# The result of set_level() is the LoggerConfig object, which immediately calls set_output().
# The result of set_output() is the LoggerConfig object, which immediately calls show_config().
#
config = LoggerConfig()
config.set_level("DEBUG").set_output("File").show_config()