Module-4
Modules: Random numbers, the time module, the math module, creating your own modules, Namespaces, Scope and
lookup rules, Attributes and the dot Operator, Three import statement variants.
Mutable versus immutable and aliasing
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.
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.
Random numbers
Here are some examples where random numbers can be used:
● 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,
● For encrypting banking sessions on the Internet
Example program to generate random numbers:
The randrange method call generates an integer between its lower and upper argument, using the same semantics as
range — so the lower bound is included, but the upper bound is excluded. All the values have an equal probability of
occurring.
range, randrange can also take an optional step argument. So let’s assume we needed a random odd number less than
100, we could say:
The random method returns a floating point number in the interval [0.0, 1.0) — the square bracket means “closed interval
on the left” and the round parenthesis means “open interval on the right”. In other words, 0.0 is possible, but all returned
numbers will be strictly less than 1.0. It is usual to scale the results after calling this method, to get them into an interval
suitable for your application. In the case shown here, we’ve converted the result of the method call to a number in the
interval [0.0, 5.0). Once more, these are uniformly distributed numbers — numbers close to 0 are just as likely to occur
as numbers close to 0.5, or numbers close to 1.0.
1
[Link] CSE Prepared by:Ashwitha A Shetty
Example to shuffle the list:
Repeatability and Testing
Random number generators are based on a deterministic algorithm — repeatable and predictable. So they’re called
pseudo-random generators — they are not genuinely random. They start with a seed value. Each time you ask for another
random number, you’ll get one based on the current seed attribute, and the state of the seed (which is one of the attributes
of the generator) will be updated.
For debugging and for writing unit tests, it is convenient to have repeatability — programs that do the same thing every
time they are run. We can arrange this by forcing the random number generator to be initialized with a known seed every
time.
This alternative way of creating a random number generator gives an explicit seed value to the object. Without this
argument, the system probably uses something based on the time.
Picking balls from bags, throwing dice, shuffling a pack of cards
Example to generate a list containing n random ints between a lower and an upper bound:
Notice that we got a duplicate in the result. Often this is wanted, e.g. if we throw a die five times, we would expect some
duplicates.
If you wanted 5 distinct months, then this algorithm is wrong. In this case a good algorithm is to generate the list of
possibilities, shuffle it, and slice off the number of elements you want:
2
[Link] CSE Prepared by:Ashwitha A Shetty
In statistics courses, the first case — allowing duplicates — is usually described as pulling balls out of a bag with
replacement — you put the drawn ball back in each time, so it can occur again. The latter case, with no duplicates, is
usually described as pulling balls out of the bag without replacement. Once the ball is drawn, it doesn’t go back to be
drawn again.
The second “shuffle and slice” algorithm would not be so great if you only wanted a few elements, but from a very large
domain. Suppose I wanted five numbers between one and ten million, without duplicates. Generating a list of ten million
items, shuffling it, and then slicing off the first five would be a performance disaster! So let us have another try:
This agreeably produces 5 random numbers, without duplicates:
The time module
To know the efficiency of the algorithm one should understand how much time each task is taking to complete.
The time module has a function called clock that is recommended for this purpose. Whenever clock is called, it returns
a floating point number representing how many seconds have elapsed since your program started [Link] way to
use it is to call clock and assign the result to a variable, say t0, just before you start executing the code you want to
measure. Then after execution, call clock again, (this time we’ll save the result in variable t1). The difference t1-t0 is
the time elapsed, and is a measure of how fast your program is running.
3
[Link] CSE Prepared by:Ashwitha A Shetty
Example:
The math module
The math module contains the kinds of mathematical functions you’d typically find on your calculator (sin, cos, sqrt,
asin, log, log10) and some mathematical constants like pi and e:
4
[Link] CSE Prepared by:Ashwitha A Shetty
Creating your own modules
All we need to do to create our own modules is to save our script as a file with a .py extension. Suppose, for example,
this script is saved as a file named [Link]:
We can now use our module, both in scripts we write, or in the interactive Python interpreter. To do so, we must first
import the module.
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 a collection of identifiers that belong to a module, or to a function. Generally, we like a namespace to
hold “related” things, e.g. all the math functions, or all the typical things we’d do with random numbers. Each module
has its own namespace, so we can use the same identifier name in multiple modules without causing an identification
problem.
We can now import both modules and access question and answer in each:
will output the following:
5
[Link] CSE Prepared by:Ashwitha A Shetty
Functions also have their own namespaces:
Running this program produces the following output:
The three n’s here do not collide since they are each in a different namespace — they are three names for three different
variables, just like there might be three different instances of people, all called “Bruce”. Namespaces permit several
programmers to work on the same project without having naming collisions.
How are namespaces, files and modules related?
Python has a convenient and simplifying one-to-one mapping, one module per file, giving rise to one namespace. Also,
Python takes the module name from the file name, and this becomes the name of the namespace. [Link] is a filename,
the module is called math, and its namespace is math. So in Python the concepts are more or less interchangeable. But
you will encounter other languages (e.g. C#), that allows one module to span multiple files, or one file to have multiple
namespaces, or many files to all share the same namespace. So the name of the file doesn’t need to be the same as the
namespace. So a good idea is to try to keep the concepts distinct in your mind. Files and directories organize where
things are stored in our computer. On the other hand, namespaces and modules are a programming concept: they help
us organize how we want to group related functions and attributes. They are not about “where” to store things, and
should not have to coincide with the file and directory structures. So in Python, if you rename the file [Link], its module
name also changes, your import statements would need to change, and your code that refers to functions or attributes
inside that namespace would also need to change.
Scope and lookup rules
6
[Link] CSE Prepared by:Ashwitha A Shetty
The scope of an identifier is the region of program code in which the identifier can be accessed, or used.
There are three important scopes in Python:
• 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.
• 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.
Variables defined inside a module are called attributes of the module
Attributes and the dot operator
Variables defined inside a module are called attributes of the module. We’ve seen that objects have attributes
too: for example, most objects have a __doc__ attribute, some functions have a __annotations__ attribute.
Attributes are accessed using the dot operator (.). The question attribute of module1 and module2 is accessed
using [Link] and [Link]. Modules contain functions as well as attributes, and the dot
operator is used to access them in the same way. seqtools.remove_at refers to the remove_at function in the
seqtools module. When we use a dotted name, we often refer to it as a fully qualified name, because we’re
saying exactly which question attribute we mean.
Three import statement variants
Here are three different ways to import names into the current namespace, and to use them:
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:
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:
But hey, with nice editors that do auto-completion, and fast fingers, that’s a small price! Finally, observe this case:
7
[Link] CSE Prepared by:Ashwitha A Shetty
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.
Classes and Objects
Classes and Objects — the Basics
Object-oriented programming
Python is an object-oriented programming language, which means that it provides features that support object-oriented
programming (OOP). It was developed as a way to handle the rapidly increasing size and complexity of software
systems, and to make it easier to modify these large and complex systems over time.
In object-oriented programming the focus is on the creation of objects which contain both data and functionality
together.
User-defined compound data types
We have already discussed str,int,float [Link] are now ready to create our own user-defined class: the Point.
Consider the concept of a mathematical point. In two dimensions, a point is two numbers (coordinates) that are treated
collectively as a single object. Points are often written in between parentheses with a comma separating the coordinates.
For example, (0, 0) represents the origin, and (x, y) represents the point x units to the right and y units up from the origin.
A natural way to represent a point in Python is with two numeric values. The question, then, is how to group these two
values into a compound object. The quick and dirty solution is to use a tuple, and for some applications that might be a
good choice. An alternative is to define a new class. This approach involves a bit more effort, but it has advantages that
will be apparent soon. We’ll want our points to each have an x and a y attribute, so our first class definition looks like
this:
Class definitions can appear anywhere in a program, but they are usually near the beginning.
The syntax rules for a class definition are the same as for other compound statements. There is a header which begins
with the keyword, class, followed by the name of the class, and ending with a colon. Indentation levels tell us where the
class ends.
If the first line after the class header is a string, it becomes the docstring of the class, and will be recognized by various
tools.
Every class should have a method with the special name __init__. This initializer method is automatically called
whenever a new instance of Point is created. It gives the programmer the opportunity to set up the attributes required
within the new instance by giving them their initial state/values. The self parameter (we could choose any other name,
but self is the convention) is automatically set to reference the newly created object that needs to be initialized.
So let’s use our new Point class now:
8
[Link] CSE Prepared by:Ashwitha A Shetty
This program prints:
The variables p and q are assigned references to two new Point objects. A function like Turtle or Point that creates a
new object instance is called a constructor, and every class automatically provides a constructor function which is named
the same as the class. It may be helpful to think of a class as a factory for making objects. The class itself isn’t an instance
of a point, but it contains the machinery to make point instances. Every time we call the constructor, we’re asking the
factory to make us a new object. As the object comes off the production line, its initialization method is executed to get
the object properly set up with its factory default [Link] combined process of “make me a new object” and “get its
settings initialized to the factory default settings” is called instantiation.
Attributes
Like real world objects, object instances have both attributes and methods. We can modify the attributes in an instance
using dot notation:
Both modules and instances create their own namespaces, and the syntax for accessing names contained in each, called
attributes, is the same. In this case the attribute we are selecting is a data item from an instance. The following state
diagram shows the result of these assignments:
9
[Link] CSE Prepared by:Ashwitha A Shetty
The expression p.x means, “Go to the object p refers to and get the value of x”. In this case, we assign that value to a
variable named x. There is no conflict between the variable x and the attribute [Link] can use dot notation as part of any
expression, so the following statements are legal:
Improving our initializer
To create a point at position (7, 6) currently needs three lines of code:
We can make our class constructor more general by placing extra parameters into the __init__ method, as shown in this
example:
Adding other methods to our class
Creating a class like Point brings an exceptional amount of “organizational power” to our programs, and to our thinking.
We can group together the sensible operations, and the kinds of data they apply to, and each instance of the class can
have its own state. A method behaves like a function but it is invoked on a specific instance, e.g. [Link](90). Like a
data attribute, methods are accessed using dot notation.
Let’s add another method, distance_from_origin, to see better how methods work:
10
[Link] CSE Prepared by:Ashwitha A Shetty
When defining a method, the first parameter refers to the instance being manipulated. As already noted, it is customary
to name this parameter self. Notice that the caller of distance_from_origin does not explicitly supply an argument to
match the self parameter — this is done for us, behind our back.
Instances as arguments and parameters
We can pass an object as an argument in the usual way.
Here is a simple function involving our new Point objects:
Converting an instance to a string
When we’re working with classes and objects, a preferred alternative is to add a new method to the class. And we don’t
like chatterbox methods that call print. A better approach is to have a method so that every instance can produce a string
representation of itself. Let’s initially call it to_string:
11
[Link] CSE Prepared by:Ashwitha A Shetty
Instances as return values
Functions and methods can return instances. For example, given two Point objects, find their midpoint. First we’ll write
this as a regular function:
12
[Link] CSE Prepared by:Ashwitha A Shetty
Now let us do this as a method instead. Suppose we have a point object, and wish to find the midpoint halfway between
it and some other target point:
While this example assigns each point to a variable, this need not be done. Just as function calls are composable, method
calls and object instantiation are also composable, leading to this alternative that uses no variables:
13
[Link] CSE Prepared by:Ashwitha A Shetty