[Go to site: main page, start]

0% found this document useful (0 votes)
4 views19 pages

Mod 4 Notes Python

This document covers Python modules, including their definition, usage, and the importance of the standard library. It explains how to generate random numbers, the concept of pseudo-randomness, and the significance of namespaces in programming. Additionally, it discusses the math module, creating custom modules, and the organization of code for better manageability and reusability.

Uploaded by

madhu010p
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views19 pages

Mod 4 Notes Python

This document covers Python modules, including their definition, usage, and the importance of the standard library. It explains how to generate random numbers, the concept of pseudo-randomness, and the significance of namespaces in programming. Additionally, it discusses the math module, creating custom modules, and the organization of code for better manageability and reusability.

Uploaded by

madhu010p
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

1BPLC105B Python Programming

Module 4
Chapter 1- Modules

 Definition: A module is a file containing Python definitions and statements designed for use in
other programs.
 Standard Library: Python includes a vast collection of built-in modules available by default.
 Examples: The turtle module (graphics) and the string module (text processing).

The Help System:


 Used to discover and explore available standard modules.
 Accessible via the built-in help() function in the Python interpreter.

Random numbers
Randomness is a key ingredient in programming whether you're making a digital dice roll,
simulating rainfall for an environmental study, or securing a banking transaction. Python handles this
through the random module.
To use these features, you first need to import the module and create a Random Number Generator
(RNG) object.
Example:
import random
rng = [Link]() # Create the 'generator' object

1. Generating Integers: randrange


This method is used when you need a whole number within a specific range. It follows the same logic
as the range() function: the lower bound is included, but the upper bound is excluded.
Standard use: [Link](1, 7) generates a number from the set {1, 2, 3, 4, 5, 6}. This is
perfect for a dice roll.

Using a step: You can add a third argument to skip numbers.


Example: [Link](1, 100, 2) generates a random odd number between 1 and 99.

2. Generating Floats: random


The random() method returns a floating-point number in the interval [0.0, 1.0).
The Interval: This means the result can be exactly 0.0, but it will always be strictly less than
1.0.
Scaling: To get a larger decimal, you simply multiply the result.
Example: [Link]() * 5.0 gives you a number in the interval [0.0, 5.0).
Distribution: These numbers are "uniformly distributed," meaning every value in the range has
an equal chance of appearing.

3. Reordering Data: shuffle


The shuffle method is used to randomize the order of elements in a list. This is commonly used for card
games or shuffling a playlist.
Requirement: It only works on mutable sequences like lists. If you have a range, you must
convert it to a list first.
Example:
cards = list(range(52)) # Create a list 0 to 51
[Link](cards) # The list is now in a random order
1BPLC105B Python Programming

Pseudo-Randomness and Seeds


Python’s random module uses a deterministic algorithm. This means if you know the starting point
and the formula, you can predict every "random" number that follows. Because of this, they are called
Pseudo-Random Number Generators (PRNGs).
The Seed: Think of the seed as the starting page in a massive book of pre-written random
numbers.
Default Behavior: If you don't provide a seed, Python usually uses the current system time.
Since the time is always changing, the numbers feel random.
Repeatability: For debugging, you want your program to behave the same way every time it
runs. By providing a fixed seed, you ensure the sequence of numbers is identical every time.
Example:
# This will produce the exact same "random" numbers every time you run it
drng = [Link](123)

Sampling: With vs. Without Replacement


In statistics and programming, how you pick items matters. Imagine a bag full of numbered balls.

1. With Replacement (Duplicates Allowed)


Each time you pick a ball, you look at the number and put it back in the bag. The next time you reach
in, you might pick the exact same ball.
Code Method: Use randrange in a loop.
Example: Throwing a die multiple times. You can definitely roll a 6 twice in a row.

2. Without Replacement (No Duplicates)


Once you pick a ball, you keep it. It cannot be picked again.
Strategy A (Small Range): If you have a small number of options (like a deck of cards),
shuffle the whole list and "slice" off what you need.
Example:
xs = list(range(1, 13)) # Months 1-12
[Link](xs)
result = xs[:5] # Pick 5 unique months
Strategy B (Large Range): If you have millions of options, shuffling a list of 10,000,000 items
would crash your computer's memory. Instead, pick a number, check if you’ve already picked it,
and if not, add it to your list.

The "Infinite Loop" Pitfall


The text asks what happens if you run:
xs = make_random_ints_no_dups(10, 1, 6)
The result is an Infinite Loop. Here is why:
1. You are asking the function to find 10 unique numbers.
2. The range you provided (1, 6) only contains 5 possible numbers (1, 2, 3, 4, 5).
3. The function will successfully find the first 5 numbers.
4. When it tries to find the 6th number, the while True loop will keep generating numbers, but
every single one will already be in the result list.
5. The if candidate not in result check will always be false, the break will never trigger, and your
program will hang forever.
Note: Always ensure that num <= (upper_bound - lower_bound) before running a "no
duplicates" loop, or use Python's built-in [Link]() which handles this error gracefully
for you!
1BPLC105B Python Programming

The time Module


When programs get large or process millions of data points, efficiency becomes a priority. We measure
this using elapsed time.

1. The Stopwatch Pattern


To measure how long a piece of code takes to execute, you follow a simple three-step pattern:
1. Capture the start time (t_0): Record the time immediately before the code starts.
2. Run the code: Execute the function or logic you want to test.
3. Capture the end time (t_1): Record the time immediately after the code finishes.
4. Calculate the difference: t_1 - t_0 gives you the total seconds elapsed.

2. A Note on [Link]() vs. time.perf_counter()


The notes mention [Link](). Just a heads-up: in modern Python (3.8 and later), [Link]() has
been removed because its behavior varied between Windows and Unix systems.
Modern standard: Use time.perf_counter(). It is a high-resolution timer specifically designed
for measuring short durations with extreme precision.
Example: Custom Sum vs. Built-in Sum
In your example, we compare a manual for loop against Python’s built-in sum() function.
Why the difference in speed?
In the results provided:
do_my_sum: ~1.55 seconds
Built-in sum: ~0.98 seconds

The Analysis:
The built-in sum() function is written in C (the language Python itself is built with). It runs at "machine
speed" without the overhead of the Python interpreter having to look up variables and check types
during every single iteration of the loop. This makes it roughly 57% faster in this scenario.
Key Takeaway: Whenever a built-in function exists in Python (like sum(), min(), or
max()), use it! It is almost always more efficient than a manual loop because it is highly
optimized at a lower level.
Refined Timing Code
If you were to write this today, here is the updated, clean version:
Example:
import time
# Our manual version
def do_my_sum(xs):
total = 0
for v in xs:
total += v
return total
sz = 10000000
testdata = range(sz)
# Timing the manual sum
t0 = time.perf_counter()
do_my_sum(testdata)
t1 = time.perf_counter()
print(f"Manual sum took: {t1 - t0:.4f} seconds")
# Timing the built-in sum
t2 = time.perf_counter()
sum(testdata)
t3 = time.perf_counter()
print(f"Built-in sum took: {t3 - t2:.4f} seconds")
1BPLC105B Python Programming

The math Module


he math module is Python's built-in toolkit for high-level mathematical operations. It provides the
same functionality you would find on a standard scientific calculator, ranging from basic square roots to
complex trigonometric functions.

Mathematical Constants
The module provides pre-defined, high-precision constants so you don't have to type them out manually.
[Link]: Represents the ratio of a circle's circumference to its diameter (pi ~ approx 3.14159).
math.e: Represents Euler's number, the base of natural logarithms (e ~ approx 2.71828).

Common Arithmetic Functions


These functions handle standard calculations involving roots and logarithms:
[Link](x): Calculates the square root of x (sqrt{x}).
math.log10(x): Calculates the base-10 logarithm of x.
[Link](x): Calculates the natural logarithm (base e) of x.

Trigonometry: Degrees vs. Radians


A common trap for programmers is that Python’s trigonometric functions (sin, cos, tan, etc.) use
radians rather than degrees.
Conversion Functions: Since most humans think in degrees, the module provides
[Link]() to convert degrees to radians and [Link]() to convert radians back to
degrees.
Example: To find the sine of 900, you must first convert it:
[Link]([Link](90)) results in 1.0.

"Pure" Functions vs. Objects


The math module works differently than the turtle or random modules you may have seen.
1. No State (Pure): Functions in the math module are "pure." This means they do not have a
"memory" or "history." Calculating [Link](16) will always result in 4.0, regardless of what
you calculated five minutes ago. Because there is no internal "state" to keep track of, you don't
need to create an object (like alex = [Link]()). You simply call the function directly from
the module.
2. Objects (Stateful): By contrast, a Turtle object has a position, a color, and a direction. A
Random object has a "seed" that changes every time you ask for a number. These require
objects because their future behavior depends on their current state.

Summary of Usage
To use these tools, you must always start your script with import math. You then access the tools using
the "dot notation":
[Link](x)
[Link](x)
[Link](x) (Arcsin or sin-1)
[Link](x) (Arccos or cos-1)

Creating your own Modules


Creating your own modules is like building your own personal library of tools. Instead of writing
the same functions over and over in every script, you can tuck them away in a separate file and call
them whenever you need them.
1BPLC105B Python Programming

How to Create and Use Your Own Modules


1. The Setup: Saving the File
To create a module, you simply save a Python script containing functions, variables, or classes.
The Rule: The filename must end in .py.
Example: Save a file as [Link] with the following content:
def remove_at(pos, seq):
"""Removes a character at a specific position in a sequence."""
return seq[:pos] + seq[pos+1:]

2. The Import: No Extensions Allowed


When you want to use those tools in a different script (or in the interactive interpreter), you use the
import statement.
The Syntax: import seqtools
Crucial Detail: You do not include the .py extension in your code. Python already knows it's
looking for a .py file. If you type import [Link], Python will get confused and throw an
error.

3. Accessing Tools (Dot Notation)


Once imported, you access the functions inside the module using the module name followed by a dot
and the function name.
Example: import seqtools
s = "A string!"
cleaned_s = seqtools.remove_at(4, s)
print(cleaned_s) # Output: 'A sting!'
This "dot notation" prevents naming collisions. You could have your own function named remove_at
in your main script, and it wouldn't clash with seqtools.remove_at.

Why Use Modules?


Manageability: Large programs can become "walls of code" that are impossible to navigate.
Breaking them into modules turns one giant problem into several small, manageable ones.

Organization: You can group related logic together. For example, all your database functions
go in db_utils.py, and all your text processing goes in text_tools.py.

Reusability: Once you write a great function in a module, every future project you work on can
use that same module without you having to copy-paste code.
A common error-File Location:
For the import statement to work, the module file ([Link]) usually needs to be in the same
folder as the script that is trying to import it. If Python can't find it in the current directory, it will give
you a ModuleNotFoundError.

Namespaces
A Namespace is one of the most important concepts in programming. It is essentially a "container"
for names (identifiers) that helps Python keep everything organized and prevent confusion.

Understanding Namespaces

What is a Namespace?
A Collection of Names: A namespace is simply a system to ensure that all the names in a
program are unique and can be used without conflict. It tracks every variable, function, and
module name currently in use.
1BPLC105B Python Programming

The "Bruce" Analogy: Imagine three different people, all named "Bruce." You don't get them
confused because they live in different houses (namespaces). In programming, you can have a
variable named n in one function and another n in a different function; they are entirely separate.

Module-Level Namespaces
Every module you create or import has its own private namespace. This is why two different modules
can use the same variable names without "crashing" into each other.
Isolation: If [Link] has a variable answer = 42 and [Link] has answer = "The Holy
Grail", Python keeps them separate.
Accessing Names: To get to these names, you use the "dot notation": [Link] vs.
[Link].

Function-Level Namespaces
Functions also create their own temporary namespaces when they are called.
Local Variables: Variables created inside a function (like n = 7) exist only within that
function's namespace.
Persistence: Once the function finishes running, its namespace is usually deleted.
Example from your text:
If you have n = 11 in your main script, and n = 7 inside function f(), the n outside
remains 11 even after f() is finished. The "outside" n and the "inside" n live in different
worlds.

The Python "One-to-One" Mapping


In Python, the relationship between files and namespaces is very straightforward:
One File = One Module = One Namespace.
If your file is named [Link], the module is called math, and the namespace is named math.
Renaming matters: If you rename [Link] to [Link], your namespace name changes
instantly, and you must update your import statements.

Logical vs. Physical Organization


It is important to keep the concepts of storage and logic separate:
Files and Directories (Physical): These organize where things are stored on your hard drive.
Modules and Namespaces (Logical): These organize how functions and variables are grouped
together in the computer's memory.
Global Context: While Python ties these together (1:1), other languages (like C#) allow one
namespace to span many different files. Keeping these concepts distinct helps you understand
how data is organized as you learn more complex languages.

Why Namespaces Matter for Teams


Namespaces allow multiple programmers to work on the same big project.
Collaboration: Programmer A can name a variable temp in their module, and Programmer B
can also name a variable temp in theirs. When they combine their work, the program won't
break because each temp stays safe inside its own module namespace.

Scope and Lookup rules


Scope refers to the specific region of a program where a variable or identifier is "visible" and can
be accessed. If you try to use a name outside its scope, Python will throw an error.

The Three Scopes of Python


Python organizes names into a hierarchy. When you use a name, Python looks through these "layers" in
a specific order:
1BPLC105B Python Programming

Local Scope: Identifiers declared inside a function. These are private to that function and are
created only when the function is running. Each function has its own unique local namespace.

Global Scope: Identifiers declared at the top level of a module or file (outside of any
functions). These are accessible to everything within that specific file.

Built-in Scope: Identifiers that are always available in Python without importing anything (e.g.,
range, min, print, len). These are the "default" tools provided by the language.

Lookup Precedence (The "Inside-Out" Rule)


If the same name exists in multiple scopes, Python uses the innermost version it finds first. This is
called Shadowing.
1. Local first: Python checks if the name exists inside the current function.
2. Global second: If not found locally, it checks the file's global level.
3. Built-in last: If still not found, it checks the built-in Python names.

Example 1: Hiding Built-ins


If you define your own function called range, Python will stop at the Global level and never reach the
Built-in range.
def range(n):
return 123 * n
print(range(10)) # Uses your GLOBAL function, not the BUILT-IN one.
# Output: 1230

Warning-While you can do this, it is considered bad practice because it confuses other programmers
who expect range to work the standard way.

Detailed Variable Example:Consider this code where names are reused in different scopes
n = 10 # Global n
m=3 # Global m
def f(n): # Local n (passed as an argument)
m=7 # Local m
return 2*n + m
print(f(5), n, m)

Why is the output 17 10 3?


Inside the function f(5): Python uses the Local n (which is 5) and the Local m (which is 7).
Calculation: 2 times 5 + 7 = 17.

Outside the function (the print statement): The function's local variables have disappeared.
Python looks in the Global scope and finds the original n = 10 and m = 3.

Visibility: The global n on line 1 is "hidden" while the function is running because the local n
takes precedence.

Scope Tools
Python provides functions to inspect in each scope:
locals(): Returns a dictionary of everything in the current local scope.
globals(): Returns a dictionary of everything in the global scope.
dir(): Lists the names in the current scope or a specified module.
1BPLC105B Python Programming

Attributes and the Dot Operator


In Python, everything is an object, and every object can have "stuff" inside it. That "stuff"
whether it’s a piece of data or a function is called an attribute. To get to that stuff, we use the Dot
Operator.

What are Attributes?


Definition: An attribute is a variable or a function that belongs to a specific module or object.
Scope: Variables defined at the top level of a module are technically attributes of that module.
Universal Access: Almost all objects in Python have built-in attributes. For example:
doc : An attribute that stores the "docstring" (documentation) of a function or
module.
annotations : An attribute that stores notes about the types of data a function expects.

The Dot Operator (.)


The dot is the syntax we use to "reach inside" an object or module to pull out its attributes. It acts as a
connector between the owner and the item.
Usage: [Link]
Examples:
[Link] (Accessing a variable named question inside module1)
[Link] (Accessing the constant pi inside the math module)
seqtools.remove_at (Accessing the function remove_at inside the seqtools module)

Fully Qualified Names


When we use the dotted name format (like [Link]), we are using what is called a fully
qualified name.
The Benefit: It is "fully qualified" because it explicitly states exactly which version of a name
you want.
Preventing Confusion: If you have a variable named answer in three different modules, the
fully qualified name ensures Python knows exactly which one to grab (e.g., [Link] vs.
[Link]).

Modules vs. Functions


It is helpful to remember that functions are just another type of attribute.
a) In the expression seqtools.remove_at(4, s), remove_at is simply an attribute of the seqtools
module that happens to be a "callable" function.
b) The dot operator treats variables and functions exactly the same way when it comes to
accessing them.

The Three Import Statement Variants


1. The Standard Import: import math
This is the preferred method for most programmers.
Why: It keeps your code explicit. When you see [Link](), you know exactly where that
function came from.
Namespace: It only adds the name math to your current namespace.

2. Selective Import: from math import cos, sin


This is useful if you only need one or two specific items and want to save some typing.
Catch: You cannot use the module name anymore. Writing [Link]() would actually cause an
error because math itself wasn't imported only its children were.
1BPLC105B Python Programming

3. The Wildcard: from math import *


This "shorthand" pulls every single function and constant from the module and dumps them into your
current namespace.
Danger: If your script already has a function named sin, and you import * from math, the math
version will "overwrite" yours without warning. This is called Namespace Pollution.

Advanced Import Tricks


Aliasing: import ... as ...
If a module name is too long, or if you want to avoid a name conflict, you can rename it during the
import.
Example: import math as m
print([Link]) # Much shorter than [Link]

Local Imports (The Scope Trap)


Where you put the import statement matters. If you put it inside a function, it stays inside that function.
Example: def get_area(radius):
import math # Local import
return [Link] * radius**2
x = [Link](10) # ERROR: 'math' is not defined in the global scope!
Rule of Thumb: Always place your imports at the very top of your file so they are available
globally to the entire script.
1BPLC105B Python Programming

Chapter 2- Mutable versus immutable and aliasing

Mutability vs. Immutability


In Python, every object has a type, and that type determines whether the object's value can be
changed after it is created.

Mutable Datatypes
Definition: Objects whose internal state or contents can be modified in place without changing
their identity.
Examples: Lists ([]), Dictionaries ({}), Sets.
Behavior: You can update, add, or delete elements directly.
Example: Changing my_list[0] = 9 updates the existing list object.

Immutable Datatypes
Definition: Objects whose state cannot be changed once they are created.
Examples: Strings ("abc"), Tuples (()), Integers, Floats.
Behavior: Any attempt to modify the content (like my_tuple[0] = 9) results in a TypeError. To
"change" an immutable object, you must create an entirely new one.

Aliasing
Aliasing occurs when more than one variable refers to the same object in memory.
The Mechanism: When you write list_two = list_one, Python does not create a new list. Instead,
it creates a second reference to the exact same memory address.
The Side Effect: Since both variables point to the same data, mutating one variable affects
the other.
Verification: You can use the built-in id() function to check the memory address. If id(list_one)
== id(list_two), they are aliases.

Avoiding Aliasing (Cloning)


To prevent unwanted changes to the original data, you must create a copy (or clone) of the object rather
than a new reference.
Shallow Copying with Slicing
For simple lists, you can use the slice operator [:]:
list_two = list_one[:] creates a new object with the same values.
Changes to list_two will no longer affect list_one because their IDs are now different.

The Nested List Limitation


A standard slice copy is a shallow copy. If a list contains other lists (nested lists), the inner lists are still
aliased.
Solution: For complex or nested structures, use the copy module (specifically [Link]()).

Sets and Frozen sets


Categorizing Python Datatypes
To understand where sets and frozensets fit, we categorize datatypes based on whether they are
Ordered (sequence-based) and Mutable (changeable).
The Datatype Matrix
Ordered Unordered

Mutable List Set (and Dictionaries)

Immutable Tuple Frozenset


1BPLC105B Python Programming

Note: While dictionaries are unordered and mutable, they are "mapping types" (Key-Value
pairs). Sets and Frozensets are "collection types" (single elements), filling the gap for unordered
collections.

Sets (Unordered & Mutable)


A set is a collection of unique elements that has no defined order.
Uniqueness: Sets automatically remove duplicate values. For example, set([1, 4, 2, 4]) results in
{1, 2, 4}.
Mutability: You can modify a set after creation using methods like .add().
No Indexing: Because they are unordered, you cannot access elements via my_set[0]. Any
"ordered" appearance in the output is coincidental.
Common Operations
Membership Testing: x in my_set (extremely fast in sets).
Iteration: for x in my_set: (order is not guaranteed).
Mathematical Operations:
Union (|): Combines elements from both sets.
Intersection (&): Finds elements common to both sets.
Difference (-): Removes elements of the second set from the first.

Frozensets (Unordered & Immutable)


A frozenset is the immutable version of a set.
Behavior: It shares the same properties as a set (unordered, unique elements), but its contents
cannot be changed once created.
Restrictions: You cannot use .add() or .remove() on a frozenset.
Use Case: Since they are immutable, frozensets can be used as keys in a dictionary or as
elements of another set, whereas a standard mutable set cannot.

Advanced Collections
For needs beyond the standard types, Python’s collections module provides specialized structures:
Stacks & Queues: For specific data handling (LIFO/FIFO).
OrderedDict: A dictionary that remembers the order in which keys were inserted.
1BPLC105B Python Programming

Chapter 3- Object oriented programming

Classes and Objects - The Basics


Object-Oriented Programming (OOP)
OOP is a programming paradigm that evolved to manage the increasing complexity of software systems.
Procedural vs. OOP:
Procedural: Focuses on functions (procedures) that operate on data. Data and logic are
often separate.
OOP: Focuses on objects that bundle both data (attributes) and functionality (methods)
together.
The Concept: Objects in code usually represent real-world objects or mathematical concepts
(e.g., a Point, a Turtle, or a Bank Account).

User-Defined Compound Data Types


While Python has built-in types (str, int, list), you can create your own types using classes.
The "Point" Example
A mathematical point (x, y) is a "compound" object because it represents two coordinates as a single
unit. While a tuple (0, 0) could work, a Class is better because it allows you to group the data with
specific operations (like calculating distance).

Class Anatomy and Syntax


A class is like a blueprint or a factory; it defines how to create objects, but it is not the object itself.
The Class Definition example:
class Point:
""" Point class represents and manipulates x,y coords. """
def init (self):
""" Create a new point at the origin """
self.x = 0
self.y = 0
 class Keyword: Starts the definition. By convention, class names are capitalized (PascalCase).
 Docstring: The first string inside a class provides documentation for tools and other programmers.
 The init Method (The Initializer): * A special method called automatically when a new
object is created.
Its job is to set up the initial state (default values) of the object.
· The self Parameter: * A reference to the specific instance being created.
It allows the code to distinguish between "this" object's variables and "that" object's variables.

Instantiation: Creating Objects


The process of creating a new object from a class is called instantiation.
Constructor: A function named after the class (e.g., Point()) that "constructs" the object.
Individual Identity: Every time you call the constructor, you get a unique object in memory.
Example: p = Point() # Create one Point object
q = Point() # Create a second, separate Point object
Even though p and q both start with x=0 and y=0, they are independent. Changing p.x will not change
q.x.

Key points:
Class: The blueprint or "factory" for an object.
Instance / Object: The actual entity created from the blueprint.
Attribute: A variable that belongs to an object (like self.x).
Instantiation: The act of calling the constructor to create a new instance.
1BPLC105B Python Programming

Attributes
Attributes and Dot Notation
Attributes are the data variables stored inside an object instance. You interact with them using dot
notation ([Link]).
Assignment: You can modify an instance’s attributes at any time after the object is created.
p.x = 3 tells Python: "Find the object referenced by p, and set its internal x
variable to 3."
Access: You can retrieve values using the same syntax.
print(p.y) tells Python: "Go to object p and give me the value of y."

The State Diagram


In memory, an object is represented as a separate container (namespace) that holds its own data.

Namespaces and Scope


One of the most powerful features of OOP is that it prevents naming conflicts.
Instance Namespaces: Every object instance has its own "hidden" dictionary of variables.
Global vs. Local: You can have a variable named x in your main code and an attribute named x
inside your object p.
x = p.x
In this statement, the first x lives in the global namespace, while p.x lives in the
instance namespace.
Unambiguous Reference: Dot notation acts as a "full address," ensuring Python always knows
exactly which x you are talking about.

Attributes in Expressions
Attributes behave just like any other variable. They can be used in calculations, print statements, or as
arguments for functions.
Example Calculation:
distance_squared = p.x2 + p.y2

String Formatting: You can inject attributes directly into strings:


print("(x={0}, y={1})".format(p.x, p.y))

Keypoints
Attribute: A piece of data (variable) belonging to an object.
Dot Notation: The syntax (.) used to access or change an attribute.
Namespace: The "container" that keeps an object's variables separate from others.

Streamlining Object Creation


In the basic version of a class, creating an object with specific data is a multi-step process. By
improving the init method, we can combine instantiation and assignment into a single line.
The "Old" Way (Inefficient):
p = Point() # Step 1: Create
p.x = 7 # Step 2: Assign x
p.y = 6 # Step 3: Assign y
The "New" Way (Efficient):
p = Point(7, 6) # Creation and assignment happen at once
1BPLC105B Python Programming

Parameterized Initializers
By adding parameters to the init method, we allow the constructor to accept data at the moment of
creation.
The Improved Syntax
def init (self, x=0, y=0):
self.x = x
self.y = y
self: Still represents the specific instance being created.
x and y parameters: These receive the values passed into the constructor (e.g., the 4 and 2 in
Point(4, 2)).
Default Values: By setting x=0 and y=0 in the header, the parameters become optional. If no numbers
are provided, the point defaults to the origin (0, 0).

The Technical Distinction: Creation vs. Initialization


There is a subtle but important difference between "creating" an object and "initializing" it:
Creation: Python sets aside space in the computer's memory for the object.
Initialization ( init ): Python fills that memory with the "factory-default" settings (your
attributes).

Note: Even though they are technically separate steps, they happen so fast and so close together
that programmers usually refer to the whole process as Instantiation.

Why This Matters for Tools


Modern IDEs (like PyScripter or VS Code) use the docstring inside your init method to help you
while you code.
a) When you type p = Point(, a tooltip often pops up showing the docstring: "Create a new point
at x, y".
b) This is why writing clear docstrings in your initializer is considered a best practice, it acts as
documentation for anyone using your "factory."
Code Result Why?

p = Point(4, 2) x=4, y=2 Values passed directly to parameters.

r = Point() x=0, y=0 No values passed; uses defaults.

Adding methods to our class


The "Organizational Power" of Methods
The primary reason to use a Class over a simple data structure (like a tuple) is the ability to group data
and functionality together.
Context Matters: A tuple (6, 7) could be a coordinate or a date (July 6th).
Logic Specificity: By using a Point class, we can attach operations like distance_from_origin()
that make sense for coordinates but would be nonsensical for a date.
Encapsulation: This grouping makes programs easier to think about because the object
"knows" what it can do with its own data.

Defining and Invoking Methods


A method is essentially a function that lives inside a class and is tied to an instance.
The Syntax
def distance_from_origin(self):
""" Compute my distance from the origin """
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
1BPLC105B Python Programming

Definition: Methods are defined inside the class block, indented to show they belong to the
class.
Invocation: Methods are called using dot notation: [Link]().
Example: p.distance_from_origin()

The Role of self in Methods


The self parameter is the bridge between the method and the specific object's data.
Automatic Passing: When you call p.distance_from_origin(), Python automatically passes p
into the self parameter "behind the scenes." You do not provide this argument manually.
Accessing State: Inside the method, self.x and self.y allow the function to access the unique
values stored in that specific instance.
Mathematical Context: The method uses the Pythagorean theorem to calculate distance:
distance = sqrt{x2 + y2}

Practical Example: Instances in Action


Each instance maintains its own state, and the method calculates results based on that specific state:
Instance Coordinates Calculation Result

p = Point(3, 4) (3, 4) sqrt{32 + 42} 5.0

q = Point(5, 12) (5, 12) sqrt{52 + 122} 13.0

r = Point() (0, 0) sqrt{02 + 02} 0.0

Adding Methods to a Class


The "Organizational Power" of Methods
The primary reason to use a Class over a simple data structure (like a tuple) is the ability to group data
and functionality together.
Context Matters: A tuple (6, 7) could be a coordinate or a date (July 6th).
Logic Specificity: By using a Point class, we can attach operations like distance_from_origin()
that make sense for coordinates but would be nonsensical for a date.
Encapsulation: This grouping makes programs easier to think about because the object
"knows" what it can do with its own data.

Defining and Invoking Methods


A method is essentially a function that lives inside a class and is tied to an instance.
The Syntax
def distance_from_origin(self):
""" Compute my distance from the origin """
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
Definition: Methods are defined inside the class block, indented to show they belong to the
class.
Invocation: Methods are called using dot notation: [Link]().
Example: p.distance_from_origin()

The Role of self in Methods


The self parameter is the bridge between the method and the specific object's data.
Automatic Passing: When you call p.distance_from_origin(), Python automatically passes p
into the self parameter "behind the scenes." You do not provide this argument manually.
Accessing State: Inside the method, self.x and self.y allow the function to access the unique
values stored in that specific instance.
1BPLC105B Python Programming

Mathematical Context: The method uses the Pythagorean theorem to calculate distance:
distance = sqrt{x2 + y2}

Practical Example: Instances in Action


Each instance maintains its own state, and the method calculates results based on that specific state:
Instance Coordinates Calculation Result
p = Point(3, 4) (3, 4) sqrt{32 + 42} 5.0
q = Point(5, 12) (5, 12) sqrt{52 + 122} 13.0
r = Point() (0, 0) sqrt{02 + 02} 0.0

Summary Table: Function vs. Method


Feature Function Method

Location Defined globally Defined inside a class

Calling Syntax function_name(arg) object.method_name(arg)

First Parameter Any variable Must be self (the instance)

Access Uses passed arguments Uses object's internal attributes via self

Instances as Arguments and Parameters


Objects as Arguments
In Python, objects can be passed into functions just like any other data type (integers, strings, etc.).
How it works: When you pass an object to a function, you are passing a reference to that
object.
The "Aliasing" Effect: Because you are passing a reference, the function does not receive a
copy of the object; it receives a pointer to the original.
If the function modifies the object (e.g., changing a turtle's color or a point's coordinate),
those changes will persist even after the function finishes.

Practical Example: print_point


Functions can be written to accept user-defined objects as parameters. This allows for customized
behavior, such as specific formatting for display.
Example:
def print_point(pt):
""" Takes a Point object and prints its coordinates nicely """
print("({0}, {1})".format(pt.x, pt.y))
The Parameter pt: Inside the function, pt acts as an alias for whatever Point instance was
passed in.
Accessing Attributes: The function uses dot notation (pt.x, pt.y) to access the data stored inside
the object.
Usage Example:
p = Point(3, 4)
print_point(p)# Output: (3, 4)

Organizational Choice: Function vs. Method


The text highlights a key architectural decision for programmers:
A Method: If the functionality is something the object does or is a core property of the object
(like calculating its own distance), it should be a method inside the class.
1BPLC105B Python Programming

A Function: If the logic is more general or handles objects in a specific context (like a
specialized print format for a report), it might be better as a standalone function that takes the
object as an argument.

Keypoints
Action Inside Class (Method) Outside Class (Function)

Call Syntax p.print_point() print_point(p)

Definition def print_point(self): def print_point(pt):

Logic Part of the object's "nature" A procedure that uses the object

Converting an Instance to a String


The "Chatterbox" Problem
In OOP, it is generally considered poor practice to write methods that directly call the print() function
(often called "chatterbox" methods).
Why? If a method prints text directly, it limits the programmer. You might want the string to
save to a file, display on a web page, or pass to a GUI-none of which require the text to appear
in the console.
The Solution: Create a method that returns a string instead of printing it. This gives the caller
the flexibility to decide what to do with that string.

The str Special Method


Python provides "magic methods" (identified by double underscores, or "dunder") to handle built-in
operations. The str method is the magic behind string conversion.
The Transformation
Initially, we might write a custom name like to_string():
Example:
def to_string(self):
return "({0}, {1})".format(self.x, self.y)
While this works, you have to remember the custom name and call it explicitly: print(p.to_string()).
Using the "Clever Trick"
By renaming the method to str , you hook into Python's internal logic:
Example:
def str (self):
return "({0}, {1})".format(self.x, self.y)

How Python Uses str


Once defined, the str method is triggered automatically in several common scenarios:
The print() function: When you call print(p), Python looks for str inside the Point class.
The str() converter: Calling str(p) will execute your custom code.
String Formatting: Using f"{p}" or "{0}".format(p) will also trigger it.

Summary of Benefits
Readability: It makes debugging much easier because you can see the "state" of the object (e.g.,
coordinates) rather than a cryptic memory address.
Consistency: Your custom objects now behave exactly like built-in types (like integers or lists)
when passed to print().
1BPLC105B Python Programming

Instances as Return Values


Creating and Returning New Objects
Functions and methods are not limited to returning simple values like integers or strings; they can also
return brand-new instances of a class. This is a common way to perform operations that result in a new
geometric or data entity.

The Midpoint Formula


To find the center between two points (x1, y1) and (x2, y2), we use:

Standalone Function vs. Class Method


The text demonstrates that the same logic can be implemented in two different ways depending on your
architectural preference.
A. As a Regular Function
Here, the function stands outside the class and takes two points as external arguments.
Example:
def midpoint(p1, p2):
mx = (p1.x + p2.x) / 2
my = (p1.y + p2.y) / 2
return Point(mx, my) # A new Point instance is born here
B. As a Class Method
Here, the method is part of the Point class. It treats the current instance (self) as the starting point and
another point (target) as the destination.
Example:
def halfway(self, target):
mx = (self.x + target.x) / 2
my = (self.y + target.y) / 2
return Point(mx, my)

Composition and Anonymous Objects


One of the most powerful features of OOP is composition. Because a method or constructor returns an
object, you can "chain" operations together without assigning every step to a variable.
Standard Approach:
p = Point(3, 4)
q = Point(5, 12)
r = [Link](q)
print(r)

Composable (Anonymous) Approach:


print(Point(3, 4).halfway(Point(5, 12)))

In this version, Python creates two "anonymous" points, finds their midpoint, prints the result,
and then clears the temporary points from memory.

Objects can have state


Defining "State"
In object-oriented programming, state refers to the data or configuration stored within an object at any
given moment.
1BPLC105B Python Programming

Static vs. Dynamic: While some attributes might stay the same, the power of an object lies in
its ability to update its state through methods.
The Turtle Example: * State: Position (x, y), heading (angle), color, and shape.
Updating State: When you call .forward(100), you aren't just moving a drawing; you
are updating the position attribute within the turtle object's state.

Practical Application: Bank Account Object


A bank account is a perfect example of why maintaining state is critical. The object acts as a
"container" for a user's financial history.
The Components of Bank State
Balance: A numeric value representing the current funds.
Transaction Log: A list (collection) of past actions, ensuring data integrity over time.

Methods that Modify State


Methods act as the "gatekeepers" for the state. Instead of just changing a number, they perform logic:
deposit(amount): Increases the balance and adds a "Deposit" entry to the log.
make_payment(amount, description): Decreases the balance and logs the description and
amount.
get_balance(): A "query" method that reports the current state without changing it.

Why State Matters


Encapsulation: By keeping the state (the balance) and the methods (the deposit logic) together,
you prevent accidental errors—like accidentally setting a balance to a negative number without
a transaction record.
Persistence: The object "remembers" what happened in previous lines of code. If you deposit
money in line 5, the object still has that money in its state when you check it in line 50.

You might also like