0% found this document useful (0 votes)
5 views11 pages

Module 4

Module 4 covers various Python programming concepts including random number generation, time and math modules, creating custom modules, namespaces, scope, and object-oriented programming basics. It emphasizes the importance of using built-in modules for efficiency and introduces key programming practices such as mutability, aliasing, and class design. The module also discusses the different ways to import modules and the implications of each method on namespace management.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views11 pages

Module 4

Module 4 covers various Python programming concepts including random number generation, time and math modules, creating custom modules, namespaces, scope, and object-oriented programming basics. It emphasizes the importance of using built-in modules for efficiency and introduces key programming practices such as mutability, aliasing, and class design. The module also discusses the different ways to import modules and the implications of each method on namespace management.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Module 4

Module 4: Random numbers, the time module, the math module, creating your
own modules, Namespaces, Scope & lookup rules, Attributes & the dot operator,
and the three import statement variants.

Random Numbers (the random module)


What is it and why do we use it
 Many programs need some element of randomness: for example simulation, games,
generating test data, or stochastic algorithms.
 Python provides a built-in module named random which encapsulates functions and
classes to generate pseudo-random numbers.
 Using a module means you don’t need to implement your own random generator (and
you benefit from tested, high-quality code).
Key functions in random
 import random — loads the module into your namespace.
 Then you can call, for example:
import random
x = [Link]() # returns a float in [0.0, 1.0)
y = [Link](1, 6) # returns integer from 1 to 6 inclusive
 Other useful functions: choice, shuffle, randrange, uniform, etc.
 Example (from text) of using [Link]() to create a random‐generator object:
import random
rng = [Link]()
dice = [Link](1,7)
 Understanding how to import and use built-in modules is a key skill.
 Random numbers allow you to explore more dynamic program behaviours (not just
deterministic ones).
 It ties into algorithms (e.g., Monte Carlo, randomized algorithms) which your students
may encounter in further courses.

The time Module


Purpose
 The time module provides access to time‐related functions: measuring elapsed time,
retrieving the current time, delays, etc.
 It is part of the Python standard library (so a module we can import like random or math).
71
Common uses
 [Link]() returns the current time in seconds (floating form) since the epoch (platform
dependent).
 [Link](seconds) pauses execution for given seconds.
 time.perf_counter() or time.process_time() can give higher‐resolution timing for
benchmarking.
 Example usage in a small program:
import time
start = [Link]()
# some code to test
end = [Link]()
print("Elapsed time: ", end - start, "seconds")

Connection to modules
 This is another example of standard library modules.
 It shows that not all modules are about “mathematical functions”—some are about
system interaction (time, OS, file system).
 Reinforces concept: import module → access module’s namespace.

The math Module


Purpose
 The math module provides mathematical functions: trigonometry, logarithms,
exponentials, constants, etc.
 Instead of writing sqrt, sin, cos yourself, you import math and leverage the existing
library.
Examples
import math
print([Link](25)) # 5.0
print([Link]([Link]/2)) # 1.0
print([Link](100, 10)) # 2.0
print([Link]) # 3.141592653589793
Why this is important
 Using the math module shows that modules are not just “nice extras” but core to writing
real code.
 It also links to algorithms and calculus/geometry topics students may have seen.

72
Creating Your Own Modules

 In addition to using built-in modules, you can create your own modules: i.e., write a .py
file that defines functions, variables, classes, and then import that file as a module in
other scripts.
 This supports code reuse, better organisation, modularity.
How to do it
1. Create a file, say [Link], containing code:
def greet(name):
print(f"Hello, {name}!")

def square(x):
return x * x

PI = 3.14159
2. In another file (or interactive session) in the same directory:
import mymodule
[Link]("Alice")
print([Link](5))
print([Link])
3. Use proper naming: module name (filename without .py) becomes the module identifier
and namespace.

Benefits
 Improves code maintainability, readability, and reuse.
 Helps students think in terms of modular design — a core software engineering practice.
 Prepares for larger projects and future courses (data structures, systems, object‐oriented
programming).

Namespaces
Definition
 A namespace is a container that holds a set of names (identifiers) and gives them
context. In Python, modules provide namespaces.
 Example: the math module defines sqrt, sin, pi, etc. All these names are in the namespace
math.

73
 They help avoid naming collisions: you can have different modules that define the same
name (e.g., sqrt) without conflict, because they live in different namespaces.
 They support clearer code: when you write [Link], you know which namespace sqrt
belongs to.
 Helps modular design: each module gets its own namespace, so internal names don’t
inadvertently clash with global program names.
Example in code
import math
import random

x = [Link](16)
y = [Link](1, 10)
Here sqrt is clearly part of math namespace; randint is part of random namespace.

Scope and Lookup Rules


Scope
 Scope refers to the region of a program where a name (variable, function) is visible / can
be used. (Wikipedia)
 In Python, common scopes include: module scope, function (local) scope, global scope,
built-in scope.
Lookup rules
 When you reference a name, Python follows a lookup rule to determine which scope to
search:
o Local (inside current function)
o Enclosing functions (if nested)
o Global module scope
o Built-in names
 This is often abbreviated as the LEGB rule (Local → Enclosing → Global → Built-in).
 Understanding scope helps avoid unexpected behaviours (e.g., shadowing a global
variable with a local, or referencing a module name that you’d re‐defined).
 When working with modules, you often have names in module namespace (global to that
module) vs names in your current script’s global scope.
Example
# module [Link]
x = 10

def f():
74
x = 20
print("inside f:", x)

print("top of module:", x) # prints 10

# script [Link]
import mymodule
print("mymodule.x =", mymodule.x) # 10
mymodule.f() # inside f: 20
Here x inside f is local; x in module global is separate.

Attributes and the Dot (.) Operator


Definition
 The dot operator in Python is used to access an attribute of an object or a module.
 For a module mymodule, [Link] accesses the function; for an object obj,
[Link] accesses a data attribute or method.
In the context of modules
 When you import a module with import math, you access its attributes: [Link], [Link],
etc.
 The module object contains attributes (names) defined within that module.
Example
import math
print([Link]) # attribute pi of module math
print([Link](1.0)) # attribute sin is a function and we call it

The Three import Statement Variants


In Python you can import modules/names in several ways. It’s important students understand the
variants, their effects on namespace, and the trade-offs.
Variant 1: import module
import math
print([Link](9))
 Pros: clear namespace, you always qualify names ([Link]) → avoids collisions.
 Cons: more typing.
Variant 2: from module import name1, name2
from math import sqrt, pi
75
print(sqrt(16))
print(pi)
 Pros: less typing, more convenient when many uses of the names.
 Cons: you bring names into your current namespace; risk of name collisions; module
origin may be less obvious.
Variant 3: from module import *
from math import *
print(sqrt(25))
print(sin(pi/2))
 Pros: easiest to type.
 Cons: very bad practice in most cases: you don’t know which names were imported, you
risk overwriting names, you degrade readability and maintainability.

Mutable vs Immutable and Aliasing


Definitions: Mutable vs Immutable
 Mutable data types are those whose contents can be changed after the object is created.
o Examples: lists, dictionaries.
 Immutable data types cannot change their contents once created.
o Examples: tuples, strings.
 If an object is mutable, operations can modify it in place — which is powerful but also
riskier.
 If an object is immutable, once created it is fixed; easier to reason about (no unexpected
side-effects).
Code examples:
# Mutable example: list
my_list = [2, 4, 5, 3, 6, 1]
my_list[0] = 9
print(my_list) # [9, 4, 5, 3, 6, 1]

# Immutable example: tuple


my_tuple = (2, 5, 3, 1)
# my_tuple[0] = 9 # This will raise an error

Aliasing
 Aliasing happens when two or more variables refer to the same object.

76
 With mutable objects, aliasing can lead to surprising behavior, because a change via one
alias affects the object seen by the other alias.
 With immutable objects, aliasing is less problematic because you cannot change the
underlying object’s contents — so sharing the same object via different names has no
side-effect risk.
Code example:
# Aliasing with mutable object
a = [1, 2, 3]
b=a # b is now an alias for the same list
print(a is b) # True — same object
b[0] = 5
print(a) # [5, 2, 3], because a and b refer to same list

# Aliasing with immutable object (example with tuple)


t1 = (1, 2, 3)
t2 = t1
print(t1 is t2) # True — same tuple object, but cannot modify it

Risks and Best Practices


Risks due to mutability + aliasing:
 Unexpected side-effects: Because multiple names refer to the same object, modifying via
one can affect others.
 Bugs that are hard to track: If one part of the code changes a shared mutable object, other
parts relying on it may break or behave incorrectly.
Best practices to mitigate these risks:
1. Avoid unnecessary aliasing when working with mutable objects — be deliberate when
you share references.
2. Clone (copy) mutable objects when you want a separate, independent instance:
o For lists, a common technique is slicing: new_list = old_list[:] — this creates a
new list.
3. Prefer immutable types when you don’t need to change the data — makes reasoning
easier.
4. Use id() or the is operator during debugging to check whether two names refer to the
same object. For example:
print(id(a), id(b))
print(a is b)

77
4. Important of Concepts
 In programming assignments, when students pass lists or dictionaries among functions,
aliasing bugs can easily creep in — understanding mutability helps prevent them.
 In data structures and algorithms, many algorithms use mutable structures (like lists) —
but incorrect sharing can lead to incorrect behavior.
 When writing modular code, being aware of mutability helps design safer APIs: whether
a function should modify in-place or return a new object.
 In software engineering, writing code that avoids unintended aliasing improves
maintainability and reduces side-effects.

Object oriented programming: Classes and Objects — The Basics, Attributes,


Adding methods to our class, Instances as arguments and parameters, Converting
an instance to a string, Instances as return values.

OOP — Classes & Objects


1. Classes and Objects — The Basics
 Class: A blueprint for creating objects. It defines a new type, specifying its data
(attributes) and behavior (methods).
 Object / Instance: A concrete realization of a class. When you “instantiate” a class, you
create an object with its own state.
 Instantiation: The process of creating an object. In Python, calling the class name (like a
function) constructs an instance and runs its initialiser.
 __init__ method: A special method in classes; called automatically when you instantiate.
It initializes the attributes of the instance.
 self parameter: Inside instance methods (including __init__), the first parameter is by
convention self, which refers to the particular instance you're working on.
Example:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y

78
p1 = Point(3, 4)
print(p1.x, p1.y) # 3 4

Attributes
 Attributes are data stored inside an object (instance). They represent its state.
 You can read or assign to attributes using the dot operator: [Link].
 Example:
p1 = Point(3, 4)
p1.x = 5 # change attribute x
print(p1.y) # access attribute y
 Each instance has its own attributes: different objects of the same class can have different
values.

Adding Methods to Our Class


 Method: A function defined inside a class that operates on instances (or the class).
 Methods typically take self as the first parameter, allowing them to read or modify
instance attributes.
 Example:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y

def move(self, dx, dy):


"""Move the point by dx, dy."""
self.x += dx
self.y += dy

def distance_from_origin(self):
return (self.x**2 + self.y**2)**0.5
Here, move changes the state (mutable), while distance_from_origin computes a value
without changing state.

Instances as Arguments and Parameters


 You can pass object instances to functions or methods just like any other parameter.
 This allows for powerful abstractions: functions or other methods can manipulate or read
objects.
79
 Example:
def print_point(pt):
print(f"({pt.x}, {pt.y})")

p = Point(2, 3)
print_point(p) # prints: (2, 3)
Inside print_point, pt is a reference to the Point instance.

Converting an Instance to a String


 It is often useful for a class to provide a method that returns a string representation of
an instance.
 In How to Think …, they define a to_string method, though in more idiomatic Python
you'd override __str__ or __repr__.
 Example (following the style of the book):
class Point:
# … (other methods) …

def to_string(self):
return f"({self.x}, {self.y})"

p = Point(4, 5)
print(p.to_string()) # "(4, 5)"
 This helps in debugging and printing; every instance can “talk about itself.”

Instances as Return Values


 Methods (or functions) can create new instances and return them.
 Use-case: given two objects, compute some relation and return another object
representing that.
 Example:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y

def halfway(self, target):


"""Return a new Point halfway between self and target."""
mx = (self.x + target.x) / 2
80
my = (self.y + target.y) / 2
return Point(mx, my)

Usage:
p = Point(3, 4)
q = Point(5, 12)
r = [Link](q)
print(r.to_string()) # "(4.0, 8.0)"
 This style promotes immutability in design of some operations: existing objects stay
unchanged; new ones are created.

Why These Concepts Are Important


 Understanding classes and objects is fundamental for software design: larger programs
are built from interacting objects, not just functions.
 For data structures and algorithms, often we will model entities (trees, graphs, points,
rectangles, etc.) as classes.
 For business information systems or projects, OOP helps represent domain concepts (e.g.,
Customer, Order, Product) naturally.
 For research code (e.g., your PhD experiments, anonymization pipelines), modular,
clean object-oriented design boosts maintainability, extensibility, and clarity.

81

You might also like