Nothing Special   »   [go: up one dir, main page]

PYTHON PROGRAMMING FOR BEGINNERS A CRASH COURSE WITH HANDS-ON PROJECTS TO LEARN PYTHON CODING, GAME PROGRAMMING WITH NO CODING... (Mike Kernell)

Download as pdf or txt
Download as pdf or txt
You are on page 1of 242

PYTHON PROGRAMMING FOR

BEGINNERS
A CRASH COURSE WITH HANDS-ON PROJECTS TO
LEARN PYTHON CODING, GAME PROGRAMMING
WITH NO CODING EXPERIENCE IN 7 DAYS TO
MASTER MACHINE LEARNING & BIG DATA
ANALYSIS
MIKE KERNELL
CONTENTS

Author Biography
Introduction

1. Introduction to Python
1.1: Python and its History
1.2: How to Install the Interpreter
1.3: How to Use the Python Shell, IDLE, and Write Your First Program
2. Variables and Operators
2.1: What are Variables?
2.2: Naming a Variable
2.3: The Assignment Operator
2.4: Basic Operators
2.5: More Assignment Operators
3. Data Types in Python
3.1: Integers
3.2: Float
3.3: String
3.4: Type Casting in Python
3.5: List
3.6: Array
3.7: Tuple
3.8: Dictionary
4. How to Make Your Program Interactive?
4.1: Input()
4.2: Print()
4.3: Triple Quotes
4.4: Escape Characters
5. Condition Statements
5.1: If statement
5.2: Inline If
6. Loops
6.1: For loop
6.2: While loop
6.3: Break
6.4: Continue
6.5: Try, Except
7. Functions and Modules
7.1: What are Functions?
7.2: Built-In Functions
7.3: Defining Your Functions
7.4: Recursive Functions
8. Variable Scope
8.1: Default Parameters Values
8.2: Variable-Length Argument List
9. Importing Modules
9.1: Creating your Own Module
10. Working with Files
10.1: Opening and Reading Text Files
10.2: Using a For Loop to Read Text Files:
10.3: Writing to a Text File
10.4: Opening & Reading Text Files by Buffer Size
10.5: Opening, Reading, and Writing Binary Files
10.6: Deleting and Renaming Files
11. Object-Oriented Programming
11.1: Classes
11.2: Objects
11.3: Polymorphism
11.4: Encapsulation
11.5: Inheritance
11.6: Multiple Inheritance
11.7: Operator Overloading
12. Data Structures and Algorithms
12.1: Asymptotic Analysis
12.2: Linear Data Structures
12.3: Non-Linear Data Structures
13. Python Gaming Project
13.1: Mario Game In Python With Source Code
13.2: Stages on how to make a Mario video game in Python
13.3: Code For Importing Libraries
13.4: Code For The Initialization & Declaration of Functions
13.5: Code For The Game Starting
13.6: Code For The Game Level
13.7: Code For The Game Main Module

Afterword
© Copyright 2022 by Mike Kernell - All rights reserved.
This document provides exact and reliable information concerning the topic and issues covered.
- From a Declaration of Principles which was accepted and approved equally by a Committee of the
American Bar Association and a Committee of Publishers and Associations.
In no way is it legal to reproduce, duplicate, or transmit any part of this document in either electronic
means or printed format. All rights reserved.
The information provided herein is stated to be truthful and consistent. Any liability, in terms of
inattention or otherwise, by any usage or abuse of any policies, processes, or directions contained
within is the solitary and utter responsibility of the recipient reader. Under no circumstances will any
legal responsibility or blame be held against the publisher for any reparation, damages, or monetary
loss due to the information herein, either directly or indirectly.
Respective authors own all copyrights not held by the publisher.
The information herein is offered for informational purposes solely and is universal as so. The
presentation of the information is without a contract or any type of guaranteed assurance.
The trademarks that are used are without any consent, and the publication of the trademark is without
permission or backing by the trademark owner. All trademarks and brands within this book are for
clarifying purposes only and are owned by the owners themselves, not affiliated with this document.
AUTHOR BIOGRAPHY

Mike Kernell, the author, has extensive technical knowledge in information


security, cryptography, algorithm analysis, design, and implementation,
graph drawing, and computational geometry, among other areas. In addition,
these are some of the things that he is interested in learning more about in the
future.
He worked as a full-time developer before transitioning to programming
books. He has also worked for a software development firm and a website
dedicated to computer science creative writing. Mike has had a long-standing
interest in computing as a conceptual framework in the classroom for many
years.
He has worked on countless numbers of software projects over the
previous 35 years in various positions. Among his many publications are
working on Python Programming, Extreme Programming, Object-Oriented
Programming, and C++ Coding. He has scores of papers published in
different trade magazines to his credit. He was a past editor of the Python
Report and presently contributes a monthly Craftsman piece to Software
Development magazine's Craftsman section.
He considers himself among such individuals one of the numerous
software specialists who give program management consulting, object-
oriented software design consulting, training, and development services to
big organizations worldwide. He participates in the community's life as a
computer science instructor. Mike got interested in computer architecture
during great innovation and change. Mike Kernell has also worked as a
technical contributor, technical manager, and technical consultant executive
with various high-technology organizations.
INTRODUCTION

Python is a powerful object-oriented language for programming that Guido


van Rossum designed. It is also termed general-purpose programming
language since it is utilized in virtually any sector we can conceive of like,
Software Development, Game Development, Web Development, AI & ML,
Data Analytics
Every Programming language provides some function or even use
according to a domain. For illustration, JavaScript is the most major language
among web developers since it allows the developer to manage applications
using numerous frameworks like Vue and rectangular to design stunning
User Interfaces. Similarly, they have positives and negatives at the same time.
If we examine Python, it is overall, which implies it is widely utilized in
every area. The reason is it's extremely easy to learn flexibly, due to which
the rate of progress is so rapid. Now you have the concept of why aside from
learning Python, it doesn't need any programming knowledge; therefore,
that's why it's famous among developers as well. Python has easier syntax
akin to English, and also the structure helps developers design applications
with fewer lines of code. Numerous code libraries simplify developers' work,
eventually leading to great productivity. They may readily concentrate on
business logic and its requiring abilities in the digital world when information
is accessible in vast data volumes.
Since its inception in 1991, Python has been a dominant force in
computer programming. Python was primarily a scripting language for
simple tasks when it was first developed. Indeed, Python isn't a brand-new
language like Rust and Golang or Dart, but it doesn't change that many still
deem it Modern. For these and many more reasons, it makes sense to follow
Python. Python's great tools and frameworks are at the heart of everything
from data science to web development to machine learning. Programming
languages such as Java and C++, better suited for server-side applications but
not for basic tasks like developing scripts or simple scripts, are not as
versatile as Python because of their extensive libraries and strong community.
Fast-paced, complete exposure to Python will get you developing programs,
solving issues, and producing things that function in a few weeks.
All one needs to know about Python programming is covered in this
book, including the kinds of data and how they interact with the operators
and statements that go with them. Python data structures and techniques,
object-oriented programming, and sorting algorithms are all covered in this
book without requiring you to put in long hours of studying computer science
theory. This book is a great place to start if you're new to Python.
Additionally, you'll get practical knowledge of Python-based game
development projects. One will also recall more easily what you've learned if
you combine work with play. The book is exceptional for beginners in the
field of study and intermediate level.
CHAPTER 1
INTRODUCTION TO PYTHON
1.1: PYTHON AND ITS HISTORY
P ython is an object-oriented programming, high-level language with
powerful semantics that can be interpreted. It incorporates high-level data
structures, dynamic binding, dynamic typing, and various other
characteristics that make it as effective for developing complex applications
as it is for scripting or "glue code" that ties components together. Python's
straightforward, easy-to-learn syntax places a premium on readability, which
lowers the cost of program maintenance. Python allows modules and
packages, promoting the modularity and reuse of code in programs.
Additionally, it may be modified to make system calls to nearly all operating
systems and execute C or C++ programs. Python is a popular language that
runs on almost every system architecture and is used in a wide range of
diverse applications due to its ubiquity and ability to operate on nearly every
system architecture.
History was going to be written in the late 1980s. It was around this
period that the development of Python began. Guido Van Rossum started his
application-based work in December 1989 at the Netherlands' Centrum
Wiskunde and Informatica (CWI). It began as a hobby project since he
sought something fascinating to keep him engaged throughout the Christmas
season. Python is supposed to have succeeded the ABC Programming
Language, which interfaced with the Amoeba Operating System and included
exception handling. He had previously assisted in creating ABC earlier in his
career, and although he saw certain flaws in the network, he admired the
majority of its qualities. Following that, what he did was quite intelligent. He
had appropriated ABC's syntax and some of its advantageous qualities. It
drew much criticism, so he resolved those faults fully and built a solid
scripting language that was free of errors. The name was inspired by the BBC
television program 'Monty Python's Flying Circus' since he was a huge
admirer of the show and desired a short, distinctive, and somewhat
mysterious name for his creation, therefore naming it Python! Guido Van
Rossom was the "Benevolent dictator for life (BDFL)" until 12th July 2018,
when he resigned as leader. He formerly worked for Google but is now
employed by Dropbox.
In 1991, the language was eventually launched. When it was launched, it
required far fewer code lines to describe ideas compared to Java, C++, and C.
Additionally; its design concept was extremely excellent. Its primary goal is
to improve code readability and developer productivity. When it was
published, it was more than capable of providing an inheritance for classes,
numerous essential data types, exception handling, and functions.
Python, one of the most commonly utilized programming languages
worldwide, has been used to construct everything from Netflix's
recommendation system to the software that runs self-driving vehicles. It is a
general-purpose language, which implies that it is intended for various
applications, including data research, software and automation, web
development, and general task completion. It is a programming language for
computers that are often used to create websites and applications, automate
operations, and undertake data analysis. It is a general-purpose language,
which means that it may be used to develop a wide range of applications and
is not specialized for any particular task. This flexibility, along with its
accessibility to beginners, has helped make it one of the most widely used
programming languages today. It is the 3rd most common programming
language in the world because it is easy to learn and easy to use; it has a
simple, straightforward syntax that is more English-like and is an open-
source language that is very versatile. There are several reasons to learn
Python, as python developers are in much demand. It could lead to a well-
paid career and offer many job opportunities, given that it can be used in
many evolving technologies, such as artificial intelligence, data analytics, and
machine learning.
1.2: HOW TO INSTALL THE INTERPRETER
Installation Steps for Windows

Step 1: Choose a Python version to download and install the Full


Installer

On Windows, you have the option of 32-bit (labeled x86) or 64-bit


(labeled x86–64) versions, as well as many installer variants for each. Find
the Latest Python 3 Release — Python 3.9.4 — under the Python Releases for
Windows.

Step 2: Install Python Executable Installer

To open the following window, double-click the downloaded executable


file. Proceed by selecting Customize installation. The Python path is
automatically specified by checking the Add Path check box.

1. Once downloaded, run the Python Installer.


2. Ensure that the checkboxes for "Install launcher for all users" and
"Add Python 3.9 to PATH" are selected.
3. Select “Install Now” from the recommended installation options.
Step 3: Wait for it to finish the installation procedure.

The following dialogue box will ask you to disable the path length limit.
By selecting this option, Python will exceed the 260-character MAX PATH
limit. In practice, this allows Python to utilizing symbolic path names.
The option to Disable the route length limit does not affect any other
system settings. The possible name length problems with Python projects will
be resolved by enabling it.

Step 4: Verification of Python's installation on Windows

1. Open command prompt in your system


2. Run “Python-V”
Installation Steps for macOS

Step 1: Visit the Downloads page in a new browser window. To


obtain the most recent version of Python, click the download
button.
Step 2: Select "Allow" when prompted to allow downloads from
“www.python.org.”
Step 3: Open up a new Finder window (the keyboard shortcut is
Command + n) and click on the sidebar for "Downloads." Then
double-click on the Python package to install it.

Step 4: This launches the Python Installer. Click the "Continue"


button.
Step 5: Following that is various displays. On the Read Me page,
click "Continue."
Step 6: Following that comes to the License page. A pop-up
window will appear by clicking "Continue," requesting your
agreement to the conditions. Select "Agree."
Step 7: Python will be saved to your disc during the standard
installation. To begin, click the "Install" button.
Step 8: This will bring up another pop-up window requesting your
password. Enter it in the appropriate field and click "Install
Software."
Step 9: Following installation, a Summary window appears. You
may close this by clicking the "Close" button.
Step 10: A popup window will ask whether you wish to delete the
Python installer. Click "Move to Trash" to remove the installer.

Additionally, the Python 3.9 package will launch a new Finder window.
Python may be accessed through the command line. Open the Terminal
application from the Applications -> Utilities -> Terminal. Once open,
execute python3 on the command line to invoke the Python interpreter. When
you see the prompt >>>, you know it is functioning. To verify, write
print("Hello, World").
1.3: HOW TO USE THE PYTHON SHELL, IDLE, AND
WRITE YOUR FIRST PROGRAM
Python Shell
Once open, execute python3 on the command line to invoke the Python
interpreter. When the prompt >>> appears, you know it is functioning. Type
print("Hello, World") to confirm. Python is a coding dialect that runs on an
interpreter. That is, it runs the code line by line. Python includes a Python
Shell that may perform a single Python command and show the result.
Additionally, it is referred to as REPL (Read, Evaluate, Print, Loop) since
it reads the command, evaluates it, publishes the result, and then loops back
to read the command again. To start the Python Shell, open the power shell or
command prompt on Windows or a terminal window on Mac, type python,
and then press enter. As seen below, a Python Prompt with three greater-than
symbols >>> displays.

Now, you can submit a single statement and get the output. For instance,
input a basic formula such as 3 + 2 and hit enter; the result will appear on the
next line, as seen below.
Python IDLE
Python IDLE's default mode of operation is the shell. When you double-
click the program's icon to launch it, the shell is the very first thing you see.
This is a new Python interpreter window with no content. You may use it
instantly to begin interacting with Python. You may verify it with the
following piece of code:

You used print() to print the string "Hello, from IDLE!" on the screen in
this case. This is the simplest method of interacting with Python IDLE. You
enter commands one by one, and Python returns the results of each
command. Following that, examine the menu bar. You'll see many choices
for interacting with the shell:
This menu allows you to restart the shell. If you choose that option, the
shell's state will be cleared. It will behave as though you've launched a new
instance of Python IDLE. The shell will completely forget about its initial
state:

In the example shown above, you define a variable, x = 5. When you use
print(x), the shell outputs the proper value, which is 5. However, when the
shell is restarted, and print(x) is attempted again, the shell produces a
traceback. This error message tells the user that the variable x is undefined.
The shell has forgotten everything that occurred before being restarted.
Additionally, you may pause the shell's execution from this menu. This
will terminate any program or statement that is currently executing in the
shell. Consider the following when you send an interrupt from the keyboard
to the shell:
At the bottom of your window, a Keyboard Interrupt error notice is
shown in red text. The program has halted execution as a result of the
interrupt.
Writing Your First Program

1. IDLE is the Python environment that will be used to write


programs. Look for the item "IDLE (Python 3.5 32-bit)" under
Python 3.5 in the Programs list.
2. The IDLE shell window is shown. Again, you may write
print("hello!") and so on, and the shell will handle the printing for
you. As you can see, it is an interactive experience. Python
executes each line of code that you input.
3. By creating a new window, a script file window is created. Print
("hello!") does not instantly generate output in this case. This is
because this is a window for editing script files, and commands
will not execute until the file is saved and executed.
4. You may launch the script by selecting "Run --> Run Module" or
by pressing F5 (or Fn + F5 on certain systems).
5. IDLE asks you to save the code as a file before execution. Select a
filename that ends in.py ("hello.py") and save it to your Desktop.
6. After that, the script will execute in the IDLE shell window.
Because you now have a stored script, you may re-run it again and
again.

Python 3.4.3 (v3.4.3:9b3f13e601, Feb 29 2020, 22:43:06) [MSC v.1600


32 bit (Intel)] on win32
>>> print ('Hello world!!!')
Hello world!!!
>>>
CHAPTER 2
VARIABLES AND OPERATORS
2.1: WHAT ARE VARIABLES?
A variable is a term that refers to a memory location. A Python variable,
also called an identifier, is used to store data. In Python, you do not need
to indicate the type of a variable since Python is an inferring language that is
intelligent enough to determine the type of a variable. Variable names may
include letters and numbers but must begin with either a letter or an
underscore. It is advised that the variable name be written in lowercase
letters. Both Rahul and Rahul are distinct variables. Python needs no
command for defining variables. It is created when you first set its value.
Variables are not required to be defined with a specific type. Variables
are nothing more than memory addresses dedicated for storing values. This
implies that when you declare a variable, you allocate memory for it. The
interpreter allocates memory and determines what may be put in reserved
memory based on the data type of a variable. As a result, by associating
variables with multiple data types, you may store integers, characters, or
decimals in these variables.
Python has three sorts of numbers: complex, floating-point, and integer.
Integers are non-decimal numbers. Floats are decimal-pointed numbers.
Complex numbers are composed of imaginary and real parts. A string is
another type of data that is very distinct from a number; it is a collection of
characters. In other words, a variable in a Python program provides data to
the computer for processing.
2.2: NAMING A VARIABLE
A variable may be designated by a short name (such as x and y) or a more
descriptive term (such as age, car name, or total volume). Variable names are
case-sensitive. You should not use the lowercase letter “i” and the uppercase
letter “O” as it may lead to confusion, and you could mistake them for a
number 1 and the second for a number 0. Letters, underscores, and digits are
permitted as variable names. However, the variable cannot begin with a
number. Underscores improve readability. For instance, “this_variable” is
more readable than “thisvariable.” It's a good idea to keep variable names as
brief as possible while still expressing the variable's function. For instance,
“member” is a better option than “m” for a variable. In the same way,
“best_choice” is preferable to “bc” as a variable name.
You want to be able to determine the variable's purpose without having
out-of-control names.
“the_finest_member_of_group_is_what_is_held_in_this_variable” is a legal
variable name but probably not a good choice to use in your programs. While
the best member of the group is what is held in this variable is technically
correct, it is probably not an acceptable variable name. Avoid using spaces or
any other kind of whitespace character in variable names. As mentioned
before, you may utilize the underscore to improve readability. Python
contains a large number of specialized functions and keywords. You must be
mindful of them to avoid using any of them as variable names in your
software! These are reserved for Python-specific purposes.
Example:
#Legal variable names:
myvar = "John"
my_var = "John"
_my_var = "John"
myVar = "John"
MYVAR = "John"
myvar2 = "John"
#Illegal variable names:
2myvar = "John"
my-var = "John"
my var = "John"
Output:

An error will occur if the opening character is not an underscore or a


letter from A-Z or a-z. For instance, the backtick (`) character:
>>> `ticked = 1
Output:

A unique identification that begins with a number is not permitted:


>>> day of week = 'Monday'
Output:

Additionally, you are not permitted to utilize restricted terms as variable


names. The term AND is a reserved word in Python. As a result, the
following assignment will fail:
>>> and = 'the winner is?'
Output:
In each unsuccessful example above, the Python interpreter threw an
exception and refused to perform the variable's assignment or creation. As
you can see, the caret character (^) corresponds to a different location inside
the incorrect identification. This is because the interpreter attempts to match
the identifier to a syntax that is allowed. However, the consequence is the
same in either case: improper variable names result in an error.
2.3: THE ASSIGNMENT OPERATOR
Operations are used to manipulate values and variables. These are the
specialized symbols that are used to perform arithmetic, logical, and bitwise
operations. The operator's operand is the value on which it works.
Assignment Variables are assigned values via operators.
To assign the value of the expression's right side to the operand on the
left, the assignment operator (=) is used.
Example:
# Assigning values using
# Assignment Operator
a=3
b=5
c=a+b
# Output
print(c)
Output:
2.4: BASIC OPERATORS
Operators are constructs that allow for the manipulation of operands' values.
Example:
>>> 2+3
Output:

Here “+” is the addition operator. 2 and 3 are the operands, whereas 5 is
the operation's outcome.
There are different kinds of operators in Python, which are listed below:
Arithmetic Operators:
Arithmetic operators are used to performing mathematical operations
such as addition, subtraction, multiplication, etc.
Example:
# Examples of Arithmetic Operator
a=9
b=4
# Addition of numbers
add = a + b
# Subtraction of numbers
sub = a - b
# Multiplication of number
mul = a * b
# Division(float) of number
div1 = a / b
# Division(floor) of number
div2 = a // b
# Modulo of both number
mod = a % b
# Power
p = a ** b
# print results
print(add)
print(sub)
print(mul)
print(div1)
print(div2)
print(mod)
print(p)
Output:

Comparison Operators:
Relational operators perform comparisons on values. It yields either True
or False depending on the criteria.
Example:
# Examples of Relational Operators
a = 13
b = 33
# a > b is False
print(a > b)
# a < b is True
print(a < b)
# a == b is False
print(a == b)
# a != b is True
print(a != b)
# a >= b is False
print(a >= b)
# a <= b is True
print(a <= b)
Output:

Logical Operators:
Logical operators execute the operations Logically AND, Logical OR,
and Logical NOT. It is used to group conditional statements.
Example:
# Examples of Logical Operator
a = True
b = False
# Print a and b is False
print(a and b)
# Print a or b is True
print(a or b)
# Print not a is False
print(not a)
Output:

Bitwise Operators:
Bitwise operators operate on bits and execute actions bit by bit. These are
used to perform binary number operations.
Example:
# Examples of Bitwise operators
a = 10
b=4
# Print bitwise AND operation
print(a & b)
# Print bitwise OR operation
print(a | b)
# Print bitwise NOT operation
print(~a)
# print bitwise XOR operation
print(a ^ b)
# print bitwise right shift operation
print(a >> 2)
# print bitwise left shift operation
print(a << 2)
Output:
2.5: MORE ASSIGNMENT OPERATORS
Assignment Operators are used to specify the values to be assigned to
variables. Here are some assignment operators listed below:

Assign: Assign value of right side of expression to left side operand.


Syntax: x = y + z
Example:
# Assigning values using
# Assignment Operator
x=7
y=9
z=x+y
# Output
print(z)
Output:

+=

Add and Assign: Add the right and left operands and then assign them to
the left operand.
Syntax: a += b
Example:
x=7
y=9
#x=x+y
x += y
# Output
print(x)
Output:

-=

Subtract and Assign: The right operand is subtracted from the left
operand, and the result is assigned to the left operand.
Syntax: x -= y
Example:
x=7
y=5
#x=x-y
x -= y
# Output
print(x)
Output:

*=

Multiply and Assign: The right operand is multiplied by the left operand,
and the result is assigned to the left operand.
Syntax: x *= y
Example:
x=3
y=5
#x=x*y
x *= y
# Output
print(x)
Output:
/=

Divide and Assign: This operator divides the left operand by the right
operand and returns the remainder to the left operand.
Syntax: x /= y
Example:
x=3
y=5
#x=x/y
x /= y
# Output
print(x)
Output:

%=

Modulus and Assign: This operator computes the modulus by combining


the left and right operands and then assigning the outcome to the left operand.
Syntax: x %= y
Example:
x=3
y=5
#x=x%y
x %= y
# Output
print(x)
Output:
//=

Divide (Floor) and Assign: This operation divides the left and right
operands and then assigns the result to the left operand.
Syntax: x //= y
Example:
x = 10
y=5
# x = x // y
x //= y
# Output
print(x)
Output:

**=

Exponent and Assign: It is used to compute the exponent (raise power)


value by multiplying the operands together and then assigning the result to
the left operand.
Syntax: x **= y
Example:
x=2
y=3
# x = x ** y
x **= y
# Output
print(x)
Output:

&=
Bitwise AND and Assign: This operator performs Bitwise AND on both
inputs before assigning the output to the left operand.
Syntax: x &= y
Example:
x=3
y=5
#x=x&y
x &= y
# Output
print(x)
Output:

|=

Bitwise OR: Performs Bitwise OR on the values before assigning the


outcome to the left operand.
Syntax: x |= y
Example:
x=3
y=5
#x=x|y
x |= y
# Output
print(x)
Output:

^=

Bitwise XOR: Operator performs Bitwise XOR on the operands before


assigning the answer to the left values.
Syntax: x ^= y
Example:
x=3
y=5
#x=x^y
x ^= y
# Output
print(x)
Output:

>>=

Bitwise Right Shift: The operator performs a bitwise right shift upon
values before assigning the answer to the left operand.
Syntax: x >>= y
Example:
x=3
y=5
# x = x >> y
x >>= y
# Output
print(x)
Output:

<<=

Bitwise Left Shift: Operator executes a bitwise left shift upon operands
before appointing the outcome to the left operand.
Syntax: x <<= y
Example:
x=3
y=5
# x = x << y
x <<= y
# Output
print(x)
Output:
CHAPTER 3
DATA TYPES IN PYTHON
3.1: INTEGERS
A ndecimals,
integer is a numeric value that may be positive or negative, without
and of any length. In Python, integers are zero, positive, or
negative whole numbers that lack a fractional portion and have an unbounded
precision, for example, 0, 100, -10. The following are valid Python integer
literals.
>>> 0
0
>>> 100
100
>>> -10
-10
>>> 1234567890
1234567890
>>>
y=5000000000000000000000000000000000000000000000000000000
5000000000000000000000000000000000000000000000000000000

Integers may take on binary, octal, or hexadecimal representations.


>>> 0b11011000 # binary
216
>>> 0o12 # octal
10
>>> 0x12 # hexadecimal
15
All integer literals and variables are int class objects. As seen below, use
the type() function to get the class name.
>>>type(100)
<class 'int'> # type of x is int

>>> x=1234567890
>>> type(x)
<class 'int'> # type of x is int

>>> y=5000000000000000000000000000000000000000000000000000000

>>> type(y) # type of y is int


<class 'int'>
The int() method takes a text or float and turns it to an int.
>>> int('100')
100
>>> int('-10')
-10
>>> int('5.5')
5
>>> int('100', 2)
4
3.2: FLOAT
Floating point numbers (float) in Python are negative and positive real
numbers with a fractional component represented by the decimal sign (.) or
the scientific symbol E or e, e.g., is 1234.56, 3.142, -1.55, 0.23.
>>> f=1.2
>>> f
1.2
>>> type(f)
<class 'float'>
Convert text, int, to float using the float() method.
>>> float('5.5')
5.5
>>> float('5')
5.0
>>> float(' -5')
-5.0
>>> float('1e3')
1000.0
>>> float('-Infinity')
-inf
>>> float('inf')
inf
3.3: STRING
Arrays of bytes in Python that correspond to Unicode types are known as
strings. Nevertheless, since Python requires a character, a distinct character is
only a one-length string. Square brackets may be utilized to retrieve the
string's components.
Creating a String:
Python allows for the creation of strings using single quotes, double
quotes, or even triple quotes.
Example:
# Python Program for
# Creation of String
# Creating a String
# with single Quotes
String1 = 'Welcome to the World of Fantasy'
print("String created using Single Quotes: ")
print(String1)
# Creating a String
# with double Quotes
String1 = "I'm a Super Hero"
print("\nString created using Double Quotes: ")
print(String1)
# Creating a String
# with triple Quotes
String1 = '''I'm a Super Hero and I live in a world of "Fantasy"'''
print("\nString created using Triple Quotes: ")
print(String1)
# Creating String with triple
# Quotes allows multiple lines
String1 = '''Nerds
For
Life'''
print("\nCreating a multiline String: ")
print(String1)
Output:
Accessing Characters in Python:
Individual characters in a string may be retrieved in Python with the
Indexing function. Indexing enables negative address references to access
characters at the end of the String; for example, -1 corresponds to the final
character, -2 to the second-to-last character, and so on. In contrast, retrieving
an index that is not inside the range results in an IndexError. Only integers
are permitted as indexes, floats, or other kinds that will result in a TypeError.
Example:
# Python Program to Access
# characters of String
String1 = "StrawberryCake"
print("Initial String: ")
print(String1)
# Printing First character
print("\nFirst character of the String is: ")
print(String1[0])
# Printing Last character
print("\nLast character of th String is: ")
print(String1[-1])
Output:
3.4: TYPE CASTING IN PYTHON
Type Casting is a technique for converting a variable's data type to a specific
data type to conduct the action requested by users.
There are two types of typecasting in Python:
Implicit Type Conversion:
The user is not required to provide any particular data type during the
implicit type conversion.
Example:
#program to demonstrate implicit type conversion
#initializing the value of w
w = 20
print(w)
print("The type of w is ", type(w)
#initializing the value of x
x = 6.5
print(x)
print("The type of b is ", type(x))
#initializing the value of y
y = 7.0
print(y)
print("The type of c is ", type(y))
#initializing the value of z
z = 3.0
print(z)
print("The type of d is ", type(z))
#performing arithmetic operations
res = w * x
print("The product of w and x is ", res)
add = y + z
print("The addition of y and z is ", add)
Output:
In the program above, the values of w, x, y, and z are initialized to check
how the values get converted on performing the operations. After that, the
data type of each one of them is checked. Finally, addition is performed on
the variables w and x and multiplication on the variables y and z. When the
above program is executed, it can be seen that the result is a float value in the
case of the product where y was an integer and z was float.
Explicit Type Conversion:
The user is expected to feed the value into a function to acquire the
desired data type. The int(), float(), and str() are mostly used for the explicit
type conversions.
Example:
#program to demonstrate explicit type conversion
#initializing the value of x
x=10.6
print("The type of 'x' before typecasting is ",type(x))
print(int(x))
print("The type of 'x' after typecasting is",type(x))
#initializing the value of y
y=8.3
print("The type of 'y' before typecasting is ",type(y))
print(int(y))
print("The type of 'y' after typecasting is",type(y))
#initializing the value of z
z=7
print("The type of 'z' before typecasting is ",type(z))
print(float(z))
print("The type of 'z' after typecasting is",type(z))
Output:

In the program above, x, y, and z are initialized. Int() and float() are used
in this program to see how explicit type conversion occurs. It can be checked
how the data type changes by running this application.
3.5: LIST
Python lists are among the most flexible data formats because they enable the
simultaneous manipulation of numerous components.
Creating Lists:
A list is generated in Python by enclosing entries in square brackets [],
separated using commas.
Example:
# list of integers
my_list = [1, 2, 3]
A list may include any number of items and may contain objects of
varying categories (float, integer, string, etc.).
Example:
# empty list
my_list = []
# list with mixed data types
my_list = [1, "Hello", 3.4]
Additionally, a list may include an item from another list. This is referred
to as a nested list.
Example:
# nested list
my_list = ["mouse", [8, 4, 6], ['a']]
Delete List Elements:
Through Python del statement, one or more entries from a list can be
removed. It is even capable of deleting the list completely.
Example:
# Deleting list items
python_list = ['p', 'r', 'o', 'b', 'l', 'e', 'm']
# delete one item
del python_list[2]
print(python_list)
# delete the entire list
del python_list
# Error: List not defined
print(python_list)
Output:

Python List Methods:


Python has many handy list functions that make working with lists
simple.
Example:
# Example on Python list methods
python_list = [3, 9, 2, 7, 9, 9, 4]
# Add 'x' to the end
python_list.append('x')
# Output: [3, 9, 2, 7, 9, 9, 4, 'x']
print(python_list)
# Index of first occurrence of 9
print(python_list.index(9)) # Output: 1
# Count of 9 in the list
print(python_list.count(9)) # Output: 3
Output:
3.6: ARRAY
An array is a group of items stored in adjacent memory locations. The notion
is to group equivalent elements. This simplifies the process of calculating the
position of each piece by simply adding an offset to a base value.
Creating an Array:
An array is created in Python by importing the array library; array
(data_type, value_list) is used to create an array using the type of data and list
of values given.
Example:
# Python program to demonstrate
# Creation of Array
# importing "array" for array creations
import array as arr
# creating an array with integer type
x = arr.array('i', [1, 2, 3])
# printing original array
print ("The new created array is : ", end =" ")
for i in range (0, 3):
print (x[i], end =" ")
print()
# creating an array with float type
z = arr.array('d', [2.5, 3.2, 3.3])
# printing original array
print ("The new created array is : ", end =" ")
for i in range (0, 3):
print (z[i], end =" ")
Output:

Adding Elements to an Array:


To add items to the Array, the built-in insert() technique may be utilized.
Insertion is a function that is used to fill an array with one or more data items.
Depending on the circumstance, a new data item may be added to the array's
beginning, end, or any given index. Additionally, append() inserts the value
supplied by its arguments to the array's end.
Example:
# Python program to demonstrate
# Adding Elements to a Array
# importing "array" for array creations
import array as arr
# array with int type
x = arr.array('i', [1, 2, 3])
print ("Array before insertion : ", end =" ")
for i in range (0, 3):
print (x[i], end =" ")
print()
# inserting array using
# insert() function
x.insert(1, 4)
print ("Array after insertion : ", end =" ")
for i in (x):
print (i, end =" ")
print()
# array with float type
z = arr.array('d', [2.5, 3.2, 3.3])
print ("Array before insertion : ", end =" ")
for i in range (0, 3):
print (z[i], end =" ")
print()
# adding an element using append()
z.append(4.4)
print ("Array after insertion : ", end =" ")
for i in (z):
print (i, end =" ")
print()
Output:
Removing Elements from an Array:
While the built-in remove() function may be used to delete entries from
the array, an exception is produced if the section does not belong in the set.
The Remove() method deletes a single element; an iterator deletes a range of
elements. Additionally, the pop() function may be used to erase and restore a
data item from an array, however it defaults to removing just the last
member. To erase a data element from a particular location in the array, the
pop() function is used with the item index as an argument.
Example:
# Python program to demonstrate
# Removal of elements in a Array
# importing "array" for array operations
import array
# initializing array with array values
# initializes array with signed integers
arr1 = array.array('i', [1, 2, 3, 1, 5])
# printing original array
print ("The new created array is : ", end ="")
for i in range (0, 5):
print (arr1[i], end =" ")
print ("\r")
# using pop() function to remove element at 2nd position
print ("The popped element is : ", end ="")
print (arr1.pop(2))
# printing array after popping
print ("The array after popping is : ", end ="")
for i in range (0, 4):
print (arr1[i], end =" ")
print("\r")
# using remove() function to remove 1st occurrence of 1
arr1.remove(1)
# printing array after removing
print ("The array after removing is : ", end ="")
for i in range (0, 3):
print (arr1[i], end =" ")
Output:

Searching Element in an Array:


The built-in index() method in Python is used to locate a data item inside
the array. The first index of the specified values in the input is retrieved.
Example:
# Python code to demonstrate
# searching an element in array
# importing array module
import array
# initializing array with array values
# initializes array with signed integers
arr1 = array.array('i', [1, 2, 3, 1, 2, 6])
# printing original array
print ("The new created array is : ", end ="")
for i in range (0, 6):
print (arr1[i], end =" ")
print ("\r")
# using index() function to print the index of when 1st occurrence of 2
appears
print ("The index of first occurrence of 2 is : ", end ="")
print (arr1.index(2))
# using index() function to print the index of when 1st occurrence of 1
appears
print ("The index of first occurrence of 1 is : ", end ="")
print (arr1.index(1))
Output:
3.7: TUPLE
In Python, a tuple is analogous to a list. The distinction between the two is
that once a tuple is allocated, its elements cannot be changed, but the contents
of a list may be changed.
Creating a Tuple:
A tuple is formed by enclosing all items (elements) in parenthesis () and
separating them with commas. While parentheses are not required, including
them is a good practice. A tuple may include any number of elements and
may contain objects of varying kinds (float, integer, string, list, etc.).
Example:
# Different types of tuples
# Empty tuple
myfirst_tuple = ()
print(myfirst_tuple)
# Tuple having integers
myfirst_tuple = (1, 2, 3)
print(myfirst_tuple)
# tuple with mixed datatypes
myfirst_tuple = (1, "Hello", 3.4)
print(myfirst_tuple)
# nested tuple
myfirst_tuple = ("mouse", [8, 4, 6], (1, 2, 3))
print(myfirst_tuple)
Output:

Additionally, a tuple may be formed without the need for parentheses.


This technique is referred to as tuple packing.
Example:
myfirst_tuple = 3, 4.6, "dog"
print(myfirst_tuple)
# tuple unpacking is also possible
a, b, c = myfirst_tuple
print(a) # 3
print(b) # 4.6
print(c) # dog
Output:

Having a single element enclosed in the parenthesis is insufficient. A


trailing comma will be required to demonstrate that this is a tuple.
Example:
myfirst_tuple = ("hello")
print(type(myfirst_tuple)) # <class 'str'>
# Creating a tuple having one element
myfirst_tuple = ("hello",)
print(type(myfirst_tuple)) # <class 'tuple'>
# Parentheses is optional
myfirst_tuple = "hello",
print(type(myfirst_tuple)) # <class 'tuple'>
Output:

Accessing Tuple Elements:


The index operator [] may be used to return a single item in a tuple,
where the indexing begins at 0. As a result, a tuple with six members will
have indices ranging from 0 to 5. Trying to retrieve an index that is not
within the tuple index range (6,7,...) will result in an IndexError. Because the
index has to be an integer, no float or other type may be used. This will cause
a TypeError to be thrown.
Example:
# Accessing tuple elements using indexing
myfirst_tuple = ('p','e','r','m','i','t')
print(myfirst_tuple[0]) # 'p'
print(myfirst_tuple[5]) # 't'
# IndexError: list index out of range
# print(myfirst_tuple[6])
# Index must be an integer
# TypeError: list indexes must be integers, not float
# myfirst_tuple[2.0]
# nested tuple
m_tuple = ("mouse", [8, 4, 6], (1, 2, 3))
# nested index
print(m_tuple[0][3]) # 's'
print(m_tuple[1][1]) #4
Output:

Python's sequences support negative indexing. The index -1 denotes the


last item, -2 the second-to-last item, and so on.
Example:
# Negative indexing for accessing tuple elements
myfirst_tuple = ('p', 'e', 'r', 'm', 'i', 't')
# Output: 't'
print(myfirst_tuple[-1])
# Output: 'p'
print(myfirst_tuple[-6])
Output:
Changing a Tuple:
Tuples, in contrast to lists, are immutable. This implies that once a tuple's
elements are allocated, they cannot be modified. However, if the item is a
changeable data type, such as a list, the nested elements may be modified.
Additionally, a tuple may be associated with several values (reassignment).
Example:
# Changing tuple values
myfirst_tuple = (4, 2, 3, [6, 5])
# TypeError: 'tuple' object does not support element assignment
# myfirst_tuple[1] = 9
# However, item of mutable element can be changed
myfirst_tuple[3][0] = 9 # Output: (4, 2, 3, [9, 5])
print(myfirst_tuple)
# Tuples can be reassigned
myfirst_tuple = ('p', 'r', 'o', 'g', 'r', 'a', 'm', 's')

# Output: ('p', 'r', 'o', 'g', 'r', 'a', 'm', 's')


print(myfirst_tuple)
Output:

The + operator may be used to join two tuples. This is referred to as


concatenation. Additionally, the * operator can be used to iterate over the
items in a tuple. Both the + and * operations result in creating a new tuple.
Example:
# Concatenation
# Output: (1, 2, 3, 4, 5, 6)
print((1, 2, 3) + (4, 5, 6))
# Repeat
# Output: ('Repeat', 'Repeat', 'Repeat')
print(("Repeat",) * 3)
Output:

Deleting a Tuple:
A tuple's elements cannot be changed. Deleting or removing entries from
a tuple is not possible. However, the term del allows for the complete
deletion of a tuple.
Example:
# Deleting tuples
myfirst_tuple = ('p', 'r', 'o', 'g', 'r', 'a', 'm', 'i', 'z')
# can't delete items
# TypeError: 'tuple' object doesn't support item deletion
# del myfirst_tuple[3]
# Can delete an entire tuple
del myfirst_tuple
# NameError: name 'myfirst_tuple' is not defined
print(myfirst_tuple)
Output:
3.8: DICTIONARY
A dictionary is an unsorted set of data values that is similar to a map. Unlike
other Data Types that include just a single value as an element, a dictionary
contains key: value pairs. To optimize the dictionary, a key-value pair is
included.
Example:
# Creating a Dictionary
# with Integer Keys
Dict = {1: 'My', 2: 'Name', 3: 'is', 4: 'John'}
print("\nDictionary with the use of Integer Keys: ")
print(Dict)
# Creating a Dictionary
# with Mixed keys
Dict = {'Name': 'June', 1: [1, 2, 3, 4]}
print("\nDictionary with the use of Mixed Keys: ")
print(Dict)
Output:

Adding Elements to a Dictionary:


The addition of components to a Python Dictionary may be accomplished
in a variety of ways. A Dictionary may be expanded one element at a time by
specifying value alongside the key, e.g., Dict[Key] = 'Value'. The built-in
update() function may be used to update an existing value in a Dictionary.
Additionally, you may add nested key values to an existing Dictionary.
Example:
# Creating an empty Dictionary
Dict = {}
print("Empty Dictionary: ")
print(Dict)
# Adding elements one at a time
Dict[0] = 'My'
Dict[2] = 'Name'
Dict[3] = 1
print("\nDictionary after adding 3 elements: ")
print(Dict)
# Adding set of values
# to a single Key
Dict['Value_set'] = 2, 3, 4
print("\nDictionary after adding 3 elements: ")
print(Dict)
# Updating existing Key's Value
Dict[2] = 'Welcome'
print("\nUpdated key value: ")
print(Dict)
# Adding Nested Key value to Dictionary
Dict[5] = {'Nested' :{'1' : 'Life', '2' : 'Simple'}}
print("\nAdding a Nested Key: ")
print(Dict)
Output:
Accessing Elements from a Dictionary:
To get access to the things included in a dictionary, refer to its key name.
Within square brackets, the key may be utilized.
Example:
# Python program to demonstrate
# accessing an element from a Dictionary
# Creating a Dictionary
Dict = {1: 'I', 'name': 'Like', 3: 'Sweets'}
# accessing a element using key
print("Accessing a element using key:")
print(Dict['name'])
# accessing an element using key
print("Accessing a element using key:")
print(Dict[1])
Output:
Removing Elements from Dictionary:
The keys from a Python Dictionary can be deleted using the del keyword.
The del keyword may be used to remove individual values from a dictionary
as well as the entire dictionary. Additionally, items in a nested dictionary may
be removed using the del keyword and specifying the nested key and the
individual key to delete from that nested dictionary.
Example:
# Initial Dictionary
Dict = { 5 : 'Welcome', 6 : 'To', 7 : 'FunCity',
'A' : {1 : 'I', 2 : 'Like', 3 : 'Funcity'},
'B' : {1 : 'Fun', 2 : 'Life'}}
print("Initial Dictionary: ")
print(Dict)
# Deleting a Key value
del Dict[6]
print("\nDeleting a specific key: ")
print(Dict)
# Deleting a Key from
# Nested Dictionary
del Dict['A'][2]
print("\nDeleting a key from Nested Dictionary: ")
print(Dict)
Output:
CHAPTER 4
HOW TO MAKE YOUR PROGRAM
INTERACTIVE?
4.1: INPUT()
T he input() function accepts user input and returns it.
Example:
name = input("Enter your name: ")
print(name)
Output:

Syntax:
The syntax of input() function is:
Syntax: input ( [prompt] )
Parameters:
input() function gets a single optional argument:
Optional: Prompt - a string that is printed to standard output (often
screen) without including the trailing newline.
Example:
# get input from the user
inputString = input('Enter a string:')
print('The inputted string is:', inputString)
Output:
4.2: PRINT()
The print() method outputs the specified object to either the standard output
terminal (screen) or a text stream file.
Example:
message = 'Python is fun'
# print the string message
print(message)
# Output: Python is fun
Output:

Syntax:
The complete syntax of print() is as follows:
Syntax: print ( *objects, sep = ‘ ‘, end = ‘\n’, file = sys.stdout, flush =
False)
Parameters:

Objects: Object to be printed.


Sep: Sep is used to separate objects. Default value: ‘ ‘
End: End is displayed at last.
File: It must be an object that implements the write ( string )
function. If this parameter is missing, sys.stdout will be utilized to
output objects to the screen.
Flush: If True, the stream is flushed forcefully. “False” is the
default setting.

Example:
print("Python is fun.")
a=5
# Two objects are passed
print("a =", a)
b=a
# Three objects are passed
print('a =', a, '= b')
Output:
4.3: TRIPLE QUOTES
Python's triple quotes can be used to span strings across many lines. It may
also be used in code for extended comments. Within the triple quotations,
special characters such as TABs, verbatim, & NEWLINEs can be used. Its
syntax is made up of three single or double quotes in a row, as the name
indicates.
Syntax: “” string”” or ”’” string”’”
Triple Quotes for Multi-Line Strings:
Example:
""" This is a long comment that will make the code seem unattractive and
difficult to read on a tiny screen. Thus it should be divided up into multi-line
strings using double triple-quotes"""
print("hello Geeks")
Output:

Triple Quotes for String Creation:


In Python, triple quotes may also be used to generate strings. The needed
characters can be converted to Python strings by enclosing them in triple
quotations. The following code demonstrates how to use triple quotes to
create strings:
Example:
str1 = """I """
str2 = """am a """
str3 = """Geek"""
# check data type of str1, str2 & str3
print(type(str1))
print(type(str2))
print(type(str3))
print(str1 + str2 + str3)
Output:
4.4: ESCAPE CHARACTERS
Use an escape character to insert characters that aren't allowed in a string. A
backslash followed by a character you wish to input an escape character. A
double quotation within a string enclosed by double quotes is an instance of
an illegal character:
Example:
# If you use double quotes in a string that is enclosed by double quotes,
you'll get an error:
txt = "We are the so-called "Vikings" from the north."
Output:

To fix this issue, use escape character \":


Example:
#The escape character allows you to utilize double quotes in places where
you wouldn't otherwise be able to:
txt = "We are the so-called \"Vikings\" from the north."
Other Escape characters in Python are:

Single Quote (\’)

Example:
txt = 'It\'s alright.'
print(txt)
Output:
Backslash ( \\ )

Example:
txt = "This will insert one \\ (backslash)."
print(txt)
Output:

New Line ( \n )

Example:
txt = "Hello\nWorld!"
print(txt)
Output:

Carriage Return ( \r )

Example:
txt = "Hello\rWorld!"
print(txt)
Output:

Tab ( \t )

Example:
txt = "Hello\tWorld!"
print(txt)
Output:

Backspace ( \b )

Example:
#This example erases one character (backspace):
txt = "Hello\tWorld!"
print(txt)
Output:
CHAPTER 5
CONDITION STATEMENTS
5.1: IF STATEMENT
I f Statement:
One of the most often used "conditional statements" in computer
languages is the "if statement" in Python. It determines whether or not
particular assertions must be performed. It tests for a specific condition, and
if it is true, the code within the "if" block will be performed; otherwise, it will
not. The if condition analyses a Boolean expression & only runs the block of
code if the expression is TRUE.
Syntax:
if expression
Statement
The condition will be converted to a "Boolean expression" in this step
(true or false). If the condition is fulfilled (true), the statement or program
inside the "if" block is run; if the condition becomes false, the statements or
program inside the "otherwise" block is executed.
Example:
num = 5
if (num < 10):
print("Num is smaller than 10")
print("This statement will always be executed")
Output:

A variable named 'Num' is defined with the value of 5 in the preceding


example, and the " if " statement checks if the no. is less than 10 or not. If the
statement is satisfied (true), the if block will execute a series of statements.
If-Else Statement:
The statement itself specifies that if a particular condition is true, then the
statements inside the "if block" should be executed, & if the condition is
false, then the "else" block should be executed. Only when the condition
becomes false will the "else" block be executed. It's the block in which you'll
take action if the condition isn't true. The Boolean expression is evaluated
using the if-else statement. If condition is TRUE, the "if" block's code will be
run; otherwise, the "otherwise" block's code will be executed.
Syntax:
If (EXPRESSION == TRUE):
Statement (Body of the block)
else:
Statement (Body of the block)
The condition will be converted to Boolean expression in this step (true
or false). If the condition is true, the statements or program inside the "if"
block will run, & if the condition is false, the statements or program inside
the "otherwise" block will run.
Example:
a=7
b=0
if (a > b):
print("a is greater than b")
else:
print("b is greater than a")
Output:

If "a" is greater than "b" in the following code, the statements in the "if"
block will be executed, but the ones in the "otherwise" block will be skipped.
Elif Statements:
Another conditional statement in Python is the "elif" statement. If the
supplied condition is false, the "elif" statement is being used to check several
conditions. It's identical to an "if-else" statement, with the exception that in
"else," the condition won't be verified, but in "elif," it will be verified. Elif
statements are identical to if-else statements, except that they examine several
conditions.
Syntax:
if (condition):
#If condition is true, execute the following statements:
elif (condition):
#When the if condition is false & the elif condition is true, a set of
statements is performed.
else:
#When both the if & elif conditions are false, a set of statements is
performed.
Example:
num = 10
if (num == 0):
print("Number is Zero")
elif (num > 5):
print("Number is greater than 5")
else:
print("Number is smaller than 5")
Output:

In the example above, a variable named 'num' has been defined with a
value of 10, and the condition in the "if" statement is verified to see whether
it becomes true. The piece (block) of code included within the "if" condition
will then be executed. If condition is false, the "elif" condition is checked. If
the condition is met, a block of code contained within the "elif" expression is
executed. If true, a block of code included within the "else" expression will
be executed.
Nested If-Else Statement:
An "if" or "if-else" statement nested inside another if/if-else block is
referred to as nested "if-else" statements. This capability is also available in
Python, which allows us to verify several conditions in a single application.
An "if" statement is included within another "if" statement, which is
contained within yet another "if" statement, and so on.
Syntax:
if(condition):
#Statements to execute if condition is true
if(condition):
#Statements to execute if condition is true
#end of nested if
#end of if
According to the aforementioned grammar, the if block will include
another if block, and so on. If block can have as many as 'n' if blocks inside
it.
Example:
i = 10
if (i == 10):
if (i < 20):
print(i, "is smaller than 20")
if (i < 21):
print(i, "is smaller than 21")
Output:

Elif Ladder:
A program with a ladder of "elif" statements or "elif" statements arranged
in the shape of a ladder, as the name indicates. Multiple expressions are
tested with this statement.
Syntax:
if (condition):
# If the condition is true, the following set of statements will be
executed:
elif(condition):
# If the if condition is false & the elif condition is true, a set of
statements will be performed.
elif(condition):
# When both the if and first elif conditions are false and the
second elif condition is true, a set of statements is performed.
elif(condition):
# If the first & second elif conditions are false, and the third elif
statement is true, a set of statements will be performed.
else:
#Set of statements to be executed when all if and elif conditions are
false
Example:
my_marks = 90
if (my_marks < 35):
print("Sorry!, You failed the exam")
elif(my_marks > 60 and my_marks > 100):
print("Passed in First class")
else:
print("Passed in First class with distinction")
Output:
5.2: INLINE IF
In Python, the if-else expression is logical. Inline if statements in Python do
not employ the ternary operator like they do in other languages. Instead, if the
condition is true, it provides just one code to analyze the first expression.
Otherwise, the second phase is evaluated.
Simple If Statement:
In a basic, if statement, a condition is specified, and the if block is
performed depending on that condition.
Syntax:
if condition: statement
if condition:
block
An "inline if statement" is one in which the expression is performed if the
condition is True.
Example:
data = 21
if data < 22: print("It's cool")
Output:

Inline If Else Statement:


Python's inline if-else statement must include an else clause.
Syntax:
x = 1 if y else 0
If you wish to keep the value of your 'x' variable the same, assign the old
'x' value.
Example:
data = 21
info = 19
print(data if info else 0)
Output:
This will output in 0 if the information is 0.
Example:
data = 21
info = 0
print(data if info else 0)
Output:
CHAPTER 6
LOOPS
6.1: FOR LOOP
I nTraversal
Python, the for loop is being used to iterate over a list, tuple, string.
is the process of iterating across a sequence.
Syntax:
for val in sequence:
loop body
"val" is a variable that obtains the value of each item in the series on each
reiteration. The loop will continue till the sequence's last item is reached. The
essence of the “for loop” is divided from the code by indentation.
Example:
# Program to find the sum of all nos. stored in a list
# List of nos.
number = [6, 5, 3, 8, 4, 2, 5, 4, 11]
# variable to store the sum
sum = 0
# iterate over the list
for val in numbers:
sum = sum+val
print("The sum is", sum)
Output:
6.2: WHILE LOOP
In Python, the while loop iterates over a code block as long as the test
condition is true.
When it isn’t recognized, how many times we'll iterate ahead of time
while the loop is utilized.
Syntax:
while test__expression:
Body of while
The test expression is tested first in the while loop. Only if the test
expression comes to True is the body of the loop entered. The "test
expression" is verified again after one cycle. This procedure is repeated until
the test expression returns False. The body of the while loop in Python is
dictated by indentation. The body begins with indentation and ends with the
first unintended line. Any non-zero value in Python is interpreted as True.
False is read as None and 0.
Example:
# Program to add natural
# numbers up to
# sum = 1+2+3+...+n
# To take input from the user,
# n = int(input("Enter n: "))
n = 10
# initialize sum and counter
sum = 0
i=1
while i <= n:
sum = sum + i
i = i+1 # update counter

# print the sum


print("The sum is", sum)
Output:
As long as the iterative variable “I” is below or equal to n, the test phase
in the preceding program will be True (10 in our program). In the loop's
body, the counter variable's value needs to be raised. This is vital information
(and mostly forgotten). If you don't, you'll find yourself in an endless cycle
(never-ending loop). Finally, the outcome is shown.
6.3: BREAK
The break statement in Python, like the classic break in C, ends the current
loop and continues execution at the following statement. The break is most
commonly used when an external situation occurs that necessitates a quick
escape from a loop. Both while & for loops can employ the break statement.
Example:
for letter in 'Python': # First Example
if letter == 'h':
break
print ('Current Letter :', letter)
var = 10 # Second Example
while var > 0:
print ('Current variable value :', var)
var = var -1
if var == 5:
break
print ("Goodbye!")
Output:
6.4: CONTINUE
In Python, the continue statement returns control to the start of the while
loop. All remaining statements in the current iteration of a loop are rejected
by the continue statement, which returns control to the start of the loop. Both
while & for loops can employ the continue statement.
Example:
for letter in 'Python': # First Example
if letter == 'h':
continue
print ('Current Letter :', letter)
var = 10 # Second Example
while var > 0:
var = var -1
if var == 5:
continue
print ('Current variable value :', var)
print ("Good bye!")
Output:
6.5: TRY, EXCEPT
Exceptions:
When your program detects an error, Python raises a number of built-in
exceptions (something in the program goes wrong). When one of these
exceptions occurs, the Python interpreter suspends the current process and
sends the control to the caller process until the problem is resolved. The
software will crash if it is not appropriately handled. Consider a software in
which function A calls the function B, which in turn calls the function C. If
the exception occurs in procedure (function) C but isn't handled there, the
exception is sent to B, who then sends it on to A. If the issue is not addressed,
an error notice appears, & our application comes to an abrupt end.
Catching Exceptions:
The try statement in Python can be used to manage exceptions. The
important operation that might result in an exception is contained in the try
clause. The code which handles exceptions is included in the except clause.
As a consequence, after the exception has been identified, you may choose
which steps to take.
Example:
# to get the type of exception
import sys

randomList = ['a', 0, 2]

for entry in randomList:


try:
print("The entry is", entry)
r = 1/int(entry)
break
except:
print("Oops!", sys.exc_info()[0], "occurred.")
print("Next entry.")
print()
print("The reciprocal of", entry, "is", r)
Output:

The values of randomList list in this application are cycled over. As


previously stated, the try block contains the code that might produce an
exception. If there is no exception, except block is bypassed, & regular flow
resumes (for last value). However, if an exception happens, the except block
will catch it (first & second values). Using the exc info() method in the sys
module, the name of the exception is output. It can be seen that 0 causes
“ZeroDivisionError” & causes “ValueError”.
Catching Specific Exceptions:
Don’t identify any specific exceptions in the except clause in the
preceding example. This isn't a smart programming approach because it will
capture all exceptions and treat each instance the same. It can be indicated
which exceptions should be caught with an except clause. If an exception
occurs, a try clause can include any no. of except clauses to handle distinct
occurrences, but only one will be performed. In an except clause, several
exceptions may be declared by using a tuple of values.
Example:
try:
1/0
except ZeroDivisionError:
print('Divided by zero')
print('Should reach here')
Output:
Raising Exceptions:
Exceptions are produced in Python programming when runtime issues
occur. The raise keyword can also be used to raise exceptions manually. You
may optionally give values to an exception to provide more information
about why it was raised.
Example:
>>> raise KeyboardInterrupt

KeyboardInterrupt
>>> raise MemoryError("This is an argument")

MemoryError: This is an argument


>>> try:
x = int(input("Enter a positive integer: "))
if x=<0:
raise value_Error("This is not a positive number!")
except value_Error as ve:
print(ve)
CHAPTER 7
FUNCTIONS AND MODULES
7.1: WHAT ARE FUNCTIONS?
I ncomplete
Python, a function is a collection of connected statements used to
a single job. It is possible to divide our software into smaller,
more modular portions with functions. As a result, as our program develops
in size and complexity, functions assist us in making it more ordered and
controllable. Furthermore, you may eliminate redundancy and make our code
more reusable with functions. In general, functions take in input, process it,
and give a result to the caller. Python has several built-in functions, such as
print () and others. You can build your functions in this programming
language, just as you do in other programming languages; these are user-
defined functions.
The fundamental concept is to group actions performed often or regularly
and package them as a function. This way, instead of writing the same code
repeatedly for various inputs, you can call the function as needed. You may
keep our variable's namespace as clean as possible when using functions. So,
function one may use the same variable as function two, and there will be no
confusion. Function two can likewise use the same variable as function one,
and there will be no confusion. Each variable I exists just when the system is
performing the specified function. When you use functions, you can test tiny
sections of your program individually and in isolation from the rest.
When calling a function, the following requirements must be followed:

Using the def keyword in conjunction with the given function


name allows us to define a function.
The name of the function must follow the identification rule.
A function accepts a parameter (argument), which may be
optional in certain cases. Function blocks are introduced with the
aid of the colon (:)and all block statements should be indented to
the same depth as the colon (:).
To return the result from the method, you need to utilize the return
clause. There may only be 1 return statement in a function at any
time.
For a function to be called, it must first be declared by the Python
interpreter. Otherwise, the Python interpreter returns an error. It is
necessary to utilize the function name headed by parenthesis to
invoke it.

def MyFunction(parameters):
function_block
return expression
7.2: BUILT-IN FUNCTIONS
The Python interpreter comes pre-loaded with a handful of methods and types
that are always accessible. When using the Python interpreter, you may
utilize a variety of preset functions that are easily accessible. You don't have
to declare these functions to utilize them; instead, you can call them by name.
Built-in functions are already present in the program. A module is a
collection of built-in functions that are bundled together. These modules are
bundled together to produce a library of information.
Following is the list of some built-in functions in Python:

The id () method returns a unique identifier for the supplied item.


It is important to note that each object in Python has its unique
identifier. When an item is formed, it is allocated a unique
identifier (id). The object's memory location is represented by the
id, which will be unique each time the program is executed.
(Except for those objects with a fixed unique id, such as integers
ranging from -5 to 256).
The ord() method returns a number corresponding to the Unicode
code of a particular character in the input string.
The len() method yields the number of elements present in a given
object. It is possible to get the set of characters contained in an
object that is a string using the len() method, which gives the set
of characters in the string.
Iterables are represented by a number, representing the total of all
things in the iterable. Especially if you are a newbie, I believe you
do not have a thorough knowledge of what Iterables and Iterators
are.
The sorted() method produces a sorted list of the iterable object
sent as an argument. You have the option of specifying whether
the order should be ascending or descending. Strings are sorted
chronologically, whereas integers are sorted numerically in this
function.
The max() method returns the object with the highest value, or the
object with the highest value in an iterable, depending on the
context.
The round() method produces a floating-point number that is a
reduced version of the supplied value with the predetermined
number of decimals, as indicated by the number of decimals
argument.
When the first parameter, the dividend, is divided by the second
argument, the divisor, the divmod() function produces a tuple
comprising the quotient and the remainder, called the quotient and
the remainder.
A message supplied by the print() function will be printed to the
screen or another output device using the print() method. The
message that must be printed might be a string or any other kind
of object. This method turns the object into a string before this is
shown in a text box on the screen.
The type() method returns the type of the object that was sent to it.
Input () is a function that enables you to accept input from the
user.
The abs() function will return the utter and total value of the
number passed to it.
The pow() function returns the computed value of x to the power
of y, i.e., xy, due to the calculation. A third argument may be
passed into this method, which returns x to the power of y
modulus z when the parameter is provided.

In this case, the dir() function produces a list of all the properties and
methods of the supplied object but does not return any values. This method
even returns built-in properties, the default properties for all objects, useful
for debugging.
7.3: DEFINING YOUR FUNCTIONS
Normally programs are developed to solve some issue or provide some
feature in programming. These functions might be general or customized to
applications. For general problems, created functions are adequate since they
are meant to handle universal challenges to every application. But, for special
demands, you need to create your code, which may be modularized as
functions, generally known as client functions.
Functions that you develop yourselves to execute particular jobs are
termed user-defined functions. Functions that come with Python are termed
built-in functions. If you employ functions created by others in the library,
they may be described as library functions. All other functions that you
develop on your own go under user-defined functions. So, your user-defined
function might be a library function to someone else. Built-in functions are a
component of the Python package; however, user-defined functions are
written by the developers as per necessity.
Code:
def sum_numbers(x,y):
sum = x + y
return sum
number1 = 5
number2 = 6
print("The addition of two numbers is", sum_numbers(number1,
number2))
Output:
Enter a number: 2.4
Enter another number: 6.5
The sum is 8.9
This section defines the function sum numbers(), which helps to add two
numbers and yields the sum of the two numbers. This is a sample of a user-
defined function. Alternatively, the two integers might be divided inside our
function (the choice is all ours). On the other hand, this technique would be
incompatible with the function's name and avoided. As a result, there would
be some uncertainty. It is advisable to title functions following the job they
are responsible for doing.
7.4: RECURSIVE FUNCTIONS
It is the technique that an application goes through when one of the phases in
that application includes calling the application itself (also known as
recursion). In this case the technique is repeated inside the method itself. A
process that runs via recursion is a 'recursive' procedure. To put it another
way, recursive functions are functions that call themselves. A recursive
function must always be able to determine when it should cease reproducing
itself.
They may be divided into two categories: indirect recursion and direct
recursion.

Direct recursion occurs when a method calls oneself directly from


inside its own body instead of indirect recursion.

Code:
def recursive_function():
recursive_function()

In contrast, indirect recursion occurs when a function calls another


function, which in turn invokes the method that was invoked in
the first place

Code:
def A_func():
B_func()
def B_func():
A_func()
A recursive function may be broken down into two types:

Base Case or the case of termination A Base Scenario is a case in


which the outcome is known or anticipated, and no recursive calls
are made.
Code:
def function1():
print("Welcome to function 2")
function2()
def function2():
print("Welcome to function 1")
function1()

Recursive case Let's see an illustration of a recursive function that


finds the factorial of an integer value. The factorial of an integer is
the total of the number factors. All the integers from one to that
no. multiplied together. For instance, the factorial of 3 is 1*2*3 =
6, while the factorial of 6 is 1*2*3*4*5*6.

Code:
def factorial(number):
if number == 1:
return 1
else:
return (number * factorial(number-1))
num = 3
print("The factorial of", num, "is", factorial(num))
Output:
The factorial of 3 is 6
CHAPTER 8
VARIABLE SCOPE
8.1: DEFAULT PARAMETERS VALUES
Y ou may give a default value for each argument when you define a
function. The following syntax is used to set default values for
parameters:
def function_name (param1, param2=value2, param3=value3, ...):
The assignment operator (=) is used to specify the default values (value2,
value3,...) for each argument in this syntax. When you call a function & send
an argument to a parameter with a default value, that argument is used rather
than the default value. If the parameter isn't given, the function will use the
default value. You must position them after other options with default values
to utilize default parameters. You'll get a syntax error if you don't. You can't
do anything like this, for example:
def function_name (param1=value1, param2, param3):
This causes syntax errors.
The welcome() method, which returns a greeting message, is defined in
the following example. The name and message arguments of the greet()
function are defined below. The message argument is set to 'Hi' by default.
The following code invokes the greet() method with the following two
arguments:
Example:
def greet(name, message='Hi'):
return f"{message} {name}"
greeting = greet('John', 'Hello')
print(greeting)
Output:

The greet() method utilizes the second parameter rather than the default
value since it is given as the second argument. The greet() method is called
without the second parameter in the following example:
Example:
def greet(name, message='Hi'):
return f"{message} {name}"
greeting = greet('John')
print(greeting)
Output:

In this situation, the greet() method utilizes the message parameter's


default value.
Multiple Default Parameters:
The greet() method is redefined with the two arguments with default
values in the following code. You may call the greet() method without any
parameters in the example below:
Example:
def greet(name='there', message='Hi'):
return f"{message} {name}"
greeting = greet()
print(greeting)
Output:

Assume you want the greet() method to produce something along the
lines of Hello there. You may create a function call like this:
Example:
def greet(name='there', message='Hi'):
return f"{message} {name}"
greeting = greet('Hello')
print(greeting)
Output:
Regrettably, it produces an unexpected result. Because the welcome()
method takes the 'Hello' parameter as the first, not the second, argument
when you supply it.
To fix this, use keyword parameters like this when using the greet()
function:
Example:
def greet(name='there', message='Hi'):
return f"{message} {name}"
greeting = greet(message='Hello')
print(greeting)
Output:
8.2: VARIABLE-LENGTH ARGUMENT LIST
Non – Keyworded Arguments (*args):
The (*) in *args denotes that in Python, you can give a variable no. of
arguments to a function. During the function call, you can supply any number
of parameters.
Example:
#program to demonstrate *args in Python
def my_func(*argp):
for i in argp:
#printing each element from the list
print(i)
#passing the parameters in function
my_func("Let","us","study","Data Science","and","Blockchain")
Output:

It is constructed a function that takes variable arguments as parameters in


the example above. Following that is used a "for loop" to print each element
(or argument in this example). Finally, it gave six strings to our function as
parameters. The required output is presented once the program has been run.
Keyworded Arguments (**kwargs):
A keyworded variable-length parameter can be sent here. The double star
(**) in **kwargs denotes that a function can take any no. of keyworded
arguments. It's similar to a dictionary, where each key corresponds to a value.
Example:
#program to demonstrate **kwargs in Python
def my_func(**kwargs):
for k,v in kwargs.items():
print("%s=%s"%(k,v))
#passing the parameters in function
my_func(a_key="Let",b_key="us",c_key="study",d_key="Data
Science",e_key="and",f_key="Blockchain")
Output:

You have constructed a function that takes "keyworded arguments" as


parameters in the example above. Following that, it is mentioned in the
functional specification that the key-value pair will be retrieved using a for a
loop. Finally, 6 main value pairs were supplied into the code. The intended
output is presented when this application is run.
As seen in the example below, *args & **kwargs can be used
simultaneously in a single program:
Example:
#program to demonstrate **kwargs in Python
def my_func(*args,**kwargs):
#displaying the value of args
print("The value of args is:",args)
#displaying the value of kwargs
print("The value of kwargs is:",kwargs)
#passing the values in function
my_func("Let","us","study",a_key="Data
Science",b_key="and",c_key="Blockchain")
Output:
It is constructed a function that takes *args & **kwargs (non-keyworded
& keyworded arguments) as parameters in the example above. Following
that got the function definition, which shows the values of both arguments.
Finally, the function has been given six parameters, three of which are strings
& the other 3 are key-value pairs. The intended output is presented when this
application is run.
CHAPTER 9
IMPORTING MODULES
9.1: CREATING YOUR OWN MODULE
A document containing Python commands and definitions is a module. A
file that encompasses Python code and the name of the module is an
example used to divide down huge applications into smaller, more
manageable files. Moreover, modules allow code to be reused. Instead of
duplicating their definitions into multiple applications, you may define your
most commonly used functions in the module & import them. Start by doing
a module. Please type the text into a text editor & save it as example.py.
Example:
# Python Module example
def add(a, b):
"""adds two
Numbers & return the result"""
result = a + b
return result
Within the example module, the add() function has been defined. The
function accepts two integers as input & returns the total of them.
How to Import Modules (in Python):
In Python, you may import the definitions from one module into another
or into the interactive interpreter. To do so, you can employ the import
keyword. In the Python prompt, write the following for importing our
previously set module example:
>>> import example
The names of functions specified in the example are not immediately
imported into the existing symbol table. Only the module name sample is
imported. Using the dot. Operator & the module name, you can get to the
function.
Example:
example.add(4,5.5)

Python comes with a large no. of standard modules. These files may be
found in the "Lib directory" of the Python installation path. Standard modules
could be imported in the same way that user-defined modules are imported.
Importing modules can be done in a variety of ways. They are as follows:
Import Statement:
You may use the import statement to import a module and the dot
operator to access the definitions within it, as explained before.
Example:
import math
print("pi value ", math.pi)
Output:

Import with Renaming:


A module can be imported by renaming it.
Example:
import math as m
print("pi value", m.pi)
Output:

The math module has been renamed to m. In certain circumstances, this


can save us time typing. The term math is not recognized in our scope, as you
can see. As a result, math.pi is incorrect, whereas m.pi is right.
Python from Import Statement:
You can import particular names from the module rather than the entire
module.
Example:
# import only pi from the math module
from math import pi
print("The value of pi is", pi)
Output:
Only the pi property from the math module was imported in this case.
You don't utilize the dot operator in these circumstances. Multiple
characteristics can also be imported, as seen below:
Example:
>>> from math import pi, e
>>> pi
3.141592653589793
>>> e
2.718281828459045
Import all Names:
Using the following technique, you may import all names(definitions)
from the module:
Example:
# import all names from standard module math
from math import *
print("The value of pi is", pi)
Output:

All of the definitions from the math module have been imported here.
Except for those that begin with an underscore, this contains all names visible
in the scope (private definitions). Using the asterisk (*) sign to import
everything is not a smart programming practice. This can result in several
identifier definitions. It also makes our code harder to read.
Module Search Path:
Python searches in numerous locations when importing a module. The
interpreter looks for the built-in module first. Python then searches a list of
directories specified in sys. Path if a built-in module also isn't found. The
following is the order of the search:

The current directory.


PYTHONPATH (a list of folders in an environment variable).
The “installation-dependent” default directory.
Example:
>>> import sys
>>> sys.path
Output:

To add our own path, you may add to and alter this list.
Reloading a Module:
During a session, the Python interpreter only imports a module once.
Things become more efficient as a result of this. To see how this works,
consider the following example. Assume the following code is in a module
called my module:
# This module shows the effect of
# multiple imports and reload
print("This code got executed")
You can now see the result of several imports.
>>> import my_module
#Output
This code got executed
>>> import my_module
>>> import my_module
You can see that our code was only run once. This means that our module
was only imported once. You'd have to reload your module if it changed
during the course of the application. Restarting the interpreter is one method
to achieve this. However, this is of little use. Python offers a more efficient
method of accomplishing this. To reload a module, you may use the imp
modules reload() function. You can accomplish it in a variety of ways:
>>> import imp
>>> import my_module
>>> import my_module

>>> imp.reload(my_module)
Dir() Built-in Function:
The Dir() method can be used to locate names declared within a module.
For example, in the module instance from before, a method called add() was
written. In the sample module, Dir can be used in the following way:
Example:
>>> dir(example)
#Output
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__name__',
'__initializing__',
'__loader__',
'__package__',
'add']
You may view a sorted list of the names here (along with add). All the
other names that start with just an underscore are the module's default Python
attributes (not user-defined). The __name__ element, for example, includes
the module's name.
Example:
>>> import example
>>> example.__name
The dir() method can be used without any parameters to find all the
names declared in our current namespace.
Example:
>>> a = 1
>>> b = "hello"
>>> import math
>>> dir()
Output:
CHAPTER 10
WORKING WITH FILES
10.1: OPENING AND READING TEXT FILES
H ow toYouOpen a Text File:
must use the built-in "open" function to open a file. The Python
files open function provides a file object with methods and properties that
may be used to conduct different file operations in Python. The open file
method in Python has the following syntax:
Syntax:
file_object = open ("filename", "mode")
Here, “filename” gives the name of the file that the file object has opened,
and “mode” is the attribute of a file object that tells you which mode a file
was opened in.
How to Read Files:
In Python, you may read a file by calling.txt file in "read mode" (r).
Step 1:
Open file in read mode:
f=open("myfile.txt", "r")
Step 2:
The mode function is utilized in the code to verify if the file is in open
mode. If that's the case, you can go on to the next step:
if f.mode == 'r':
Step 3:
For accessing files in Python, use f.read to read data from a file and put it
in a variable called content.
contents =f.read()
Step 4:
For a Python read text file, print the contents.
Example:
def main():
f=open("myfile.txt","r")
if f.mode=="r":
contents=f.read()
print(contents)
if __name__=="__main__":
main()
Output:

How to Read a File Line by Line:


If your.txt file is too large to read, you can read it line by line. The
readlines() function separates your data into easy-to-read sections.
Example:
def main():
f=open("myfile.txt","r")
f1=f.readlines()
for x in f1:
print(x)
if __name__=="__main__":
main()
Output:

When you run code (f1=f.readlines()) in Python to read a file line by line,
it separates each line and displays the file legibly; because the line is short &
readable in our scenario, the output will resemble the read mode, this piece of
code, however, might be beneficial if there is a complicated data file that is
not understandable.
10.2: USING A FOR LOOP TO READ TEXT FILES:
When the open() procedure is used to start a file, it returns an iterable object.
Iterating through a file object in for loop is the ultimate method of reading
in a file line by line. You're utilizing a built-in Python method that allows us
to iterate through file object implicitly using for loop in conjunction with the
iterable object in this way. This method requires fewer lines of code, which is
usually a good habit to follow.
Example:
# Python program to
# demonstrate reading files
# using for loop
L = ["Python\n", "Text\n", "File\n"]
# Writing to file
file1 = open('myfile.txt', 'w')
file1.writelines(L)
file1.close()
# Opening file
file1 = open('myfile.txt', 'r')
count = 0
# Using for loop
print("Using for loop")
for line in file1:
count += 1
print("Line{}: {}".format(count, line.strip()))
# Closing files
file1.close()
Output:
10.3: WRITING TO A TEXT FILE
To write into a file in Python, you must open it in write "w," append "a,"
and exclusive creation "x" mode. Because the "w" mode will overwrite a file
that already exists, it should be used with caution. As a consequence, all
previous information is erased. To produce a string/series of bytes, use the
write() method. This function returns the no. of characters written to the file.
Example:
with open("test.txt",'w',encoding = 'utf-8') as f:
f.write("my first file\n")
f.write("This file\n\n")
f.write("contains three lines\n")
#Output
'my first file\nThis file\n\ncontains three lines\n'
If the file "test.txt" does not exist in the current directory, this application
will create it. If it exists, it has been overwritten. To separate the different
lines, you must insert the newline characters ourselves.
10.4: OPENING & READING TEXT FILES BY BUFFER
SIZE
By setting buffer size in the read function, you may read a file with a
restricted buffer. It reads the no. of bytes you require from the pointer's
current location in the file.
Example:
with open('test.txt', 'r') as f:
print(f.read(10)) # Read and print 10 bytes
Output:
10.5: OPENING, READING, AND WRITING BINARY
FILES
Write a Binary File:
You must first write a file before you can read it. The file gets opened
using file = open("document.bin","wb"), and the binary file is written using
the "wb" mode. The name of the file is document.bin. "This is nice" has been
assigned to a variable in the form of a sentence. sentence = bytearray("This is
excellent") is used to decode the sentence. The function encodes ("ASCII")
has been used. The file.write() function was used to save the sentence to the
file. The write() method is used to save a file with the provided text. The
file.close() function was then used to close the file.
Example:
file = open("document.bin","wb")
sentence = bytearray("This is good".encode("ascii"))
file.write(sentence)
file.close()
Read a Binary File:
To read the file, take the document.bin file that has already been prepared
& read the binary file using the "rb" mode. The file name is document.bin.
The read() function is also utilized. The read() function reads a specified
no. of bytes from a file and returns them.
Example:
file = open("document.bin","rb")
print(file.read(4))
file.close()
Output:
10.6: DELETING AND RENAMING FILES
The OS module in Python offers methods for performing file-processing
activities, including renaming and removing files. To utilize this module, you
must first import it before using any associated functions.
The rename() Method:
The current filename & the new filename are sent to the rename()
function.
Syntax:
os.rename (current_file_name, new_file_name)
The following is an instance of how to rename a file called test.txt that
already exists:
Example:
#!/usr/bin/python
import os
# Rename a file from test.txt to test2.txt
os.rename( "test.txt", "test1.txt" )

The remove() Method:


By giving the name of the information to be destroyed as an argument to
the remove() function, you may delete files.
Syntax:
os.remove (file_name)
The following is an instance of how to delete the file test1.txt:
Example:
#!/usr/bin/python
import os
# Delete file test1.txt
os.remove("test1.txt")
CHAPTER 11
OBJECT-ORIENTED PROGRAMMING

A lan Kay developed the phrase "Object-Oriented Programming" (OOP) in


1966 when he was a graduate student at Stanford University. Simula was
the very first programming language to have the concepts of object-oriented
programming, and it was developed in the 1960s. It was created in 1967 to
create simulation programs, where the most relevant information was denoted
by the term "objects."
Because of C++, even though object-oriented programming (OOP) has
been around since the early 1960s, it wasn't until the 1990s that OOP really
took off. Various programming languages have adopted this programming
technique, including Python. There are instances of it in practically every
area., including real-world systems, machine intelligence, intelligent agents,
client devices, object-oriented database systems, and many others.
Programming in Python leverages object-oriented programming (OOPs)
as a programming paradigm. Programming concepts such as inheritance,
polymorphisms, and Encapsulation will be included in the language. When
using OOPs, the content and the methods that act on it are bound together
like a single unit such that no other section of the code may access it.
Data and objects are the foundations of object-oriented programming
(OOP), a paradigm that has emerged in recent years. Programming languages
should contain mechanisms for dealing with classes and objects and the
implementation and use of key object-oriented ideas and concepts such as
inheritance, abstraction, Encapsulation, and Polymorphism if they are
considered object-oriented.
Learn how to create Python objects that may be used across your
application by defining custom types in Python classes and then instantiating
those classes.
The following are the most important Python OOPs concepts:

Class
Object
Method
Inheritance
Encapsulation
Polymorphism
Data Abstraction
11.1: CLASSES
Data and functions can be organized into classes in Python. It allows for the
creation of data structures that can contain any data and are, as a result, more
openly accessible.
A bank employee, for example, might go into the bank's website and click
on the customer class to see all of the client's properties, such as transaction
information and withdrawal and deposit records.
There is no doubt that classes are indeed a pattern or blueprint for
creating things. In another way, we create a class that contains the properties
and methods of several objects. Variables represent attributes, whereas
methods carry out operations. There are two types of variables and methods
in classes. When you construct objects from a class, you have access to all
the same attributes and methods. To make them stand out from other data in
the instance, they are termed instance variables' (i.e., object). A method is not
a function; they are two distinct concepts.
The overall structure of a class may be summarized thusly:
Class NameOfClass(object):
attributes def __init__(self):
def procedure1():
def procedure2():
11.1.1: Class Creation
Writing the Classname after the keyword class creates a new kind of
class. The word 'object' is inserted within the Classname after the word
'Classname.' In Python, all classes are descended from a basic class called
object. We even have our classes derived from the object' class. Thus, the
parentheses need to include a reference to the 'object'. To avoid confusion, it
is unnecessary to use the word "object."
Code example for class creation and instantiation is as follows:
class Student:
# Class attribute
type = "student"
# Instance attribute
def __init__(self, name, age):
self.name = name
self.age = age

# Instantiate the Student class


Ali = Student("Ali", 10)
Zara = Student("Zara", 15)
# Access the class attributes
print("Ali is a {}”. format (Ali.__class__.type))
print("Zara is also a {}”. format(Zara.__class__.type))
# Access the instance attributes
print ("{} is {} years old”. format (Ali.name, Ali.age))
print ("{} is {} years old”. format (Zara.name, Zara.age))

Instance methods operate on a class's instances (or objects). The memory


address of the instance may be found. Instance methods may access the
instance variables since they know where the instance is.
The methods init (self), and talk(self) was formerly referred to as
"instance methods" in the preceding source code. The __init__() function in
the student class is used by a student to provide us with his or her name and
age. In this picture, he is presenting himself to us. Ali is now enrolled as a
student. As of this age, Ali has reached the age of 10. You're not doing
enough if you only write a class like this. It ought to be used. To use a class,
we must first construct a class instance. When you create an instance of a
variable, you allocate the memory needed to hold the actual contents of that
variable. To begin, a chunk of heap memory is allotted. To determine how
much memory is required, look at the student class's characteristics and
methods.
__init(self)' is called internally once the memory block has been
allocated. The initial data is saved in the variables using this way. This
technique is referred to as a 'function Object() { [native code] }' since it is
helpful in creating an instance.
Finally, the instance's allocated main memory address is returned to
the's1' variable. We may use the id() function to view this memory address in
decimal system format (s1).
11.1.2: Class Instance
The def __init__ does not include a full list of all possible properties for
our object, which should be considered.
You may not add additional attributes to an object after it has been
formed in certain languages because the class definition requires you to
supply a list of the object's attributes. An object in Python may be enhanced
dynamically by adding additional properties and functions. In reality, the
__init__ method has nothing to do with attribute setting. The object's age
might be cached using the age function:
def Age(self):
if attribute (self, "_age"):
return self._age
today = datetime.date. Today()
age = today.year - self.birthdate.year
if today < datetime.date(today.year, self.birthdate.month,
self.birthdate.day):
age -= 1
self. _age = age
return age
A method that creates new attributes and does not initialize them in
__init__ is considered poor practice. Updating the values of an object's
attributes is very frequent. To make matters worse, setting properties outside
of an object violates the object-oriented paradigm.
11.1.3: Class Attributes
Attributes are also defined for the class. Each instance of that class will
have these characteristics. However, there are several limitations that you'd
be aware of while using them. Class attributes are defined at the same
indentation level as method declarations in the main body.
class Car:
MODELS = ('Xli', 'Gli', 'Reborn', 'Altis')
def __init__ (self,name, year):
if model not in self. MODELS:
raise Valueerror(" Is not a valid model.")
self.name = name
self. year = year
As you can see, we use the self-variable to retrieve the class
attribute MODELS exactly as we would retrieve an instance attribute.
MODELS is a class property applied to all of our Car instances.
In many cases, class attributes are used to designate constants that belong
to a certain class. Class characteristics may be accessed through class
instances, but they can also be accessed directly from class objects if desired.
11.1.4: Creating a Class
In the same way that the def keywords define a method in Python, a class
in Python is defined by the class keyword followed by the name of the class
defined.
Docstrings are used in classes in a manner that is quite similar to that of
functions. The usage of documentation strings (docstrings) is not required,
but it is strongly advised since it is believed to be great training to add a short
explanation of the class to promote clarity and ease of understanding of the
source code.
class NameOfClass:
pass
class A:
a=5
def functionA(self):
print('Welcome to Class A')
#accessing attributes using the class object of the same name
A.functionA(1)
print(A.a)
Output:

11.1.5: Types of Classes


There are many different sorts of classes in Python, with the following
being a few examples:
11.1.6: Abstract Class
When working with object-oriented programming, abstraction is a notion
that is utilized to keep the inner functionality of classes hidden from the users'
view. In this case, the users are acquainted with the objective of the class's
methods, but they are not aware of how the methods accomplish the purpose,
which implies that they are aware of the inputs and anticipated outputs, but
the inner workings of the methods are concealed from them.
The abstract classes are used to implement abstraction in a program. It is
customary in Python to build abstract classes to specify a methodology that
must be implemented by any child classes built atop the abstract class. In a
similar vein, an abstract methodology is a technique that does not have any
associated implementation.
Abstract methods compel the subclasses to include the execution of these
ways in their own instances, which aids in the achievement of abstraction
since each subclass may provide its own instance of the method. An abstract
class consists of more than one abstract method and many abstract methods.
Examples of this may be seen in everyday life, such as when we click a
button on a TV set to change the station. We aren't concerned with how the
remote does it; we are just concerned with the fact that when we click a
certain button, the channel changes!
Smartphones are another example of abstraction; we are not conscious of
any of the phone's inherent operations; instead, we are just focused on what
we need to do to complete a certain job.
For example, we have no idea how the phone captures a video when we
push the record button; we just touch or hit a button and perform what it is
supposed to. There are a plethora of additional instances of abstraction that
we may see in action in the actual world.
Based on the examples provided above, we can conclude that abstraction
makes things better user-friendly and less complicated. Generally speaking,
abstraction is used in programming to make the lives of other developers
easier by abstracting away the details of the code. For example, in Python, we
don't have to worry about understanding how the sort() method of the list
classes works; we can just use it and sort a list for us.
Python is not a completely object-oriented programming language
because we may create Python code without defining any classes. However,
Python does support all of the concepts associated with object-oriented
programming, including abstraction and the concept of abstract classes.
In Python, it is not possible to explicitly build an abstract class. A module
in Python enables us to create abstract classes, albeit it is not often used. The
abc(abstract base class) modules in Python is the one that we may use to
construct an abstract class from scratch.
First, we will create the Shape class and then Circle and Triangle classes
under it:
from abc import ABC, abstractmethod
class Shape(ABC):
def __init__(self, shape_name):
self.shape_name = shape_name
@abstractmethod
def Create(self):
from Circle import Circle
circle = Circle()
circle.Create()
Class Triangle(Shape):
def __init__(self):
super().__init__("Triangle")
def Create(self):
print("Drawing a Triangle")
from Circle import Circle
from Triangle import Triangle
#create a circle object
circle = Circle()
circle.draw()
#create a triangle object
triangle = Triangle()
triangle.draw()
The importance of abstract classes in Python is stated below:

The abstract class acts as a blueprint for the creation of further


classes. In the case of an abstract class, you may build a generic
structure without providing entire implementations for all of the
methods. In an abstract class, concrete methods are defined as
methods that offer common functionality that all derived classes
may utilize, while abstract methods are defined as methods whose
implementation may differ from that of the concrete methods.
Since abstract classes cannot be instantiated, it is not feasible to
build objects that are instances of abstract classes.
Ordinarily, abstract methods declared in an abstract class do not
have a body, but it is viable to develop abstract methods that have
an implementation in the abstract class. It is still necessary for
every subclass inheriting from such an abstract class and provide
the implementation for the abstract methods.
The Python interpreter raises an exception if the derived class
does not define any generic method.
Abstract classes may have concrete and abstract methods, which
can be combined.

11.1.7: Partial Class


Python has a useful package called functools that is designed to be
entertaining. The partial class is one of the classes in this hierarchy. It may be
used to build a new function that applies just a portion of the parameters and
words you supply. It is possible to "freeze" a piece of your
methods arguments or keywords using the partial keyword, which creates a
new object. Another way to say it is that it partially generates a new
method with a few defaults in its parameters.
This section will develop a simple addition function that produces the
sum of its two inputs, x, and y. Following that, we create a new callable by
making an object of partial and handing it our function and input for that
function to execute. In other words, we are essentially setting the value of the
x argument of our addition function to integer 2 by default. We next call our
new callable, p addition, with the number 4, which results in the 6 since 3 + 4
= 7 due to the addition. from functools import partial
def addition(x, y):
return x + y
p_addition = partial(addition, 3)
p_addition(4)
11.1.8: Python Concrete Class
Abstract classes may have concrete and abstract methods, which can be
combined. Concrete methods are available exclusively in function Object()
classes, but abstract methods are available in both concrete and abstract
methods. However, although the concrete Class is responsible for
implementing abstract methods, the abstract class may also do so by starting
the methods via super ().
11.1.9: Class Advantages over Functions

When you use functions, they are placed at the top of your code,
and the data you provide to the functions may be located
anywhere throughout the program. When a program grows too
lengthy, it becomes difficult to supervise. Classes allow you to tie
specific data to specific functions, allowing you to keep
everything in one place.
The fundamental premise of object orientation is that types
(classes) serve as the focal point of modularity, instead of
procedures or functions, rather than processes and functions.
The fact that it is very adaptable is one of its advantages. The type
is represented, but there are no members in it. Any function has
the authority to determine which fields should be included.
Different invocations of this class may generate objects of this
kind, populated in various ways.
11.2: OBJECTS
11.2.1: Introduction to Objects
When it comes to Python, classes are merely logical entities that act as a
prototype or template for creating objects, whereas an object is a group of
variables and Python methods. Parameters and functions are declared inside a
class and are accessible via object references. Attributes are a term used to
refer to a collection of variables and functions. Objects are simple collections
of data (parameters) and ways (functions) that operate on the data in a
collection of variables. Class definitions are similar to that of an object
definition.
To better comprehend the notion of Python objects and classes, let's look
at an example. One way to think about an object is to be an ordinary
everyday item, such as a vehicle. As previously described, we know that a
class has data and functions specified inside it. These data and functions may
be deemed characteristics and behaviors of the object, respectively, based on
their placement within the class. The characteristics (data) of the automobile
(object) include its color, price, number of doors, etc. Car (object) actions
(functions) include moving at a certain speed, using the brakes, and so on. An
object class allows for creating several objects, each having its own set of
data and methods associated with it.
Previously, we learned that class objects could be used to retrieve various
characteristics.
It may also be used to generate new object instances (instantiations) of a
class that has been defined before. The technique for creating an object is
identical to calling a function.
Tom = Dog ()
This will result in creating a new individual object with the name Tom.
The characteristics of objects may be accessed by using the prefix of the
object's name.
Data or a procedure may be used as attributes. An object's procedures
correspond to the equivalent functionalities of the object's class.
11.2.2: Object Instantiation
Whenever we declare a class, we create a representation or a blueprint for
the described thing. There are no modes until we construct the object that will
be allocated. Actual information or data is included inside the objector
instance. A class may be thought of as a template. The ability to construct
data structures based on the properties and methods that you describe is
provided by this feature.
Instantiation is nothing more than the process of producing a new object
or instance of a given class.
You might think of it as a cookie-cutter that you can customize to create
the ideal cookies, each with specific attributes such as shape, size, and other
qualities that you specify.
Code:
class Cookie:
pass
cookie1=Cookie ()
cookie2=Cookie ()
On the other hand, there are some examples. An instance of a class is a
single object belonging to a class with a distinct memory address.
11.2.3: Creating an Object
To access the characteristics of a class, an object with the same identity as
the class is used. But that is not the only thing that the class object can be
used for; it could also be used to construct new objects, but then those objects
may be used to access the attributes, as seen in the following example:
Code:
class A:
a=5
def functionA(self):
print('Welcome to Class A')
object1 = A()
object1.functionA()
Output:
However, while calling the function, we are not giving any value to it
since we use a self-defined parameter when declaring it in the class. When a
function is called with an object, the object itself is immediately supplied to
the function as an argument, making object1.functionA() equal to
object1.functionA() (object1). As a result, the very first parameter in the
method must be the item itself, which is referred to as the 'self' argument in
most cases. It is possible to call it anything else as well. However, naming
itself is a tradition, and following this convention is regarded as great
programming training.
11.2.4: Modifying and Deleting Objects
In addition to creating, it is possible to modify and remove objects once
they have been created as follows:
The object can be deleted using the "del" keyword
class A:
a=5
def functionA(self):
print("Welcome to Class A")
del a
object1 = A()
del object1
#The object can be modified as:
class Apple:
a=5
def functionApple(self):
print('Welcome to Class Apple')
Apple.functionApple(1)
print(Apple.a)
Apple.a=10
print(Apple.a)
Output:
11.2.5: Advantages of Objects

When you use classes, it is much easier to maintain all of the


associated data and methods in one place, which aids in making
the program more structured.
In addition to providing inheritance, the use of classes further
enhances the capabilities of the object-oriented programming
paradigm.
Classes may also be used to override any standard operators that
are available.
A tidy structure to the code is created by grouping relevant
functions and keeping them in one location (inside a class),
resulting in increased program readability.
When classes are used, they allow the code to be reused, which
increases the overall efficiency of the application.
Classes allow you to arrange your program into generic, reusable
chunks of code using a logical structure. The greatest of these are
reusable software snippets that can be reused time and time over
with no alteration to the original code.
11.3: POLYMORPHISM
Polymorphism is defined as the capacity to take on many shapes and forms.
When using Python's polymorphism feature, we may create functions in the
child class with the same name as methods specified in their parent class.
Object-oriented programming language heavily relies on this notion,
which is extensively used. Polymorphism is implemented in Python, as it is
in other programming languages such as Java and C+, for various purposes,
the most popular of which are Duck Typing, Operator overloading,
Operator overloading, and Operator overriding. Overloading and overriding
are the two most common techniques through which a program may
accomplish Polymorphism.
An easy interpretation of Polymorphism is as follows:
2+5
7
" 2" +" 5"
25
" ab" +" xy"
abxy
We can perceive from the above example; the addition operation is used
in various contexts.
When two integer values are supplied to the Operator, the Operator
performs an addition operation. This is seen in the first example.
The second example uses the identical values as the first, but instead of
sending them as the information is in the format of a string, it uses the same
Operator to join the two strings together.
In the third case, the data set consists of two strings, but the Operator is
the same as in the previous examples, and it performs the combination of the
two strings supplied in the first two examples.
11.3.1: Need for Polymorphism
Whenever the subject of OOP in Python is brought up, the word
Polymorphism is almost always used. Objects in object-oriented
programming must take on a variety of shapes and sizes. This characteristic is
quite useful in software development, as you may imagine. Polymorphism
allows a single action to be executed in various ways. This notion is often
used when discussing loose coupling, and interfaces, dependency injection,
among others.
11.3.2: Types of Polymorphism
Overloading in Polymorphism may be classified into two categories

Compile Time Polymorphism


Run Time Polymorphism

11.3.3: Compile Time Polymorphism


Method Overloading is a technique used in Oop languages to establish
static Polymorphism, which allows the developer to implement several
methods in a single object. Although the name they use may be the same, the
parameters they utilize are not. Static Polymorphism may occur under certain
situations, which are listed below:

The sorts of all attributes should be distinct from one another.


It is possible to have an alternative sequence of Parameters.
The number of variables in one technique should be different from
the set of elements in the other method.

In the static binding, the overloaded functions are invoked based on the
type and number of arguments that match the corresponding type and number
of arguments.
Because all of this data is accessible throughout the compilation process,
the compiler finds the most suited function for the situation.
It is accomplished by function overloading, which is sometimes referred
to as static or early binding in certain circles.
class A:
def Say(self):
pass
class B(A):
def Say(self):
print("I am class B")
class C(A):
def Say(self):
print("I am class C")
a1 = B()
a1.Say()
a1 = C()
a1.Say()
Output:

The display () function prototype in the preceding program is the same in


both the base and derived classes, as shown. As a result, static binding could
not be used in this situation. This program will likely execute optimally at run
time if the functions are selected appropriately.
During runtime, the language's compiler determines the existence of
particular methods by examining the signatures of the methods in question.
During the software compilation process, the compiler first detects the
method signature and then determines which method to call for a given
method call.
Although the execution time for Compile time Polymorphism is much
shorter, the technique is less flexible.
11.3.4: Run-Time Polymorphism
Method overriding, also called runtime polymorphism, is a feature of
several programming, including C++, Python, and Java. It is referred to as
such in many of these languages.
When it comes to Python, one of the most important features is an
inheritance; when a child class inherits membership variables and member
methods from its existing class, this is referred to as inheritance. A method in
a parent class may also be overridden if we consider that the functionality
provided by the function in the base classes is no longer required.
Furthermore, overriding member functions in a child class to satisfy the
criteria of the parent class are referred to as function overriding in the
programming language.
class Bike:
def capacity(self):
print("Bikes are suitable for maximum 2 people")
def fuel(self):
print("Bike needs fuel to run")
class petrol_bike(Bike):
def fuel(self):
print("Petrol bike runs on petrol")
class electric_bike(Bike):
def fuel(self):
print("Electric bikes run on a battery")
bike=Bike()
ebike=electric_bike()
pbike=petrol_bike()
bike.fuel()
bike.capacity()
ebike.fuel()
ebike.capacity()
pbike.fuel()
pbike.capacity()
Output:

The bike is the main base class in the preceding code, while the electric
bike and petrol bike are the children classes in the same code. And the
methods fuel() and capacity() are passed down to the child classes Electric
bike and petrol bike, respectively. Currently, we can alter the functionality of
the methods as needed, and we've actually implemented the fuel() method
inside the electric bike class, and we've done the same thing in the petrol bike
class to make the fuel() function more accessible.
Examples include the code ebike. fuel(), which prints "Electric bikes
operate on a battery," and pbike.fuel(), which prints "Petrol bikes run on
gasoline."
11.3.5: Implementing Method Overloading
Python does not offer method overloading in the same way other
programming languages do. It will just overwrite the previously defined
function with the most recent definition. On the other hand, we may attempt
to obtain a result comparable to overloading by utilizing *args or optional
arguments.
class OptionalArgument:
def addNumbers(self, a, b, c=0):
return a + b + c
o = OptionalArgument()
print(o.addNumbers(2,3))
print(o.addNumbers(2,3,4))
Output:
11.3.6: Implementing Method Overriding
Method Overriding or Runtime polymorphism is the overriding of
methods. It functions in conjunction with inheritance. In method overriding,
even if the name of the method and the arguments supplied are the same, the
method's behavior varies depending on the kind of object being overridden.
class Animal:
def sound (self):
pass
class Cat(Animal):
def sound (self):
print("Meoooowwwww")
class Dog(Animal):
def sound (self):
print("Woohoo")
a = Cat()
a.sound()
a = Dog()
a.sound()
Output:

We constructed the Cat and Dog classes in the preceding example, which
derive from the Animal class. After that, we added the Sound method to both
the Cat and Dog classes. As you can see in the preceding example, sound
publishes various results on the same Animals reference depending on how it
is configured in sound.
At runtime, it determines the kind of object to be created and runs the
appropriate function.
11.3.7: Why Should one use Polymorphism?

Increased the program's adaptability


The effectiveness of a technique varies depending on the situation.
Polymorphism decreases coupling and reusability by coding to an
interface, making your code simpler to comprehend while
increasing reusability and readability. The method's context is
shaped depending on the data given.
Polymorphism is an intrinsically beneficial trait. It refers to
anything that may be expressed in various ways, including objects
and techniques.
It allows a method component in a class with multiple kinds to
have the same name.
The objectives of Polymorphism in object-oriented programming
are to enforce simplicity while also making scripts more flexible
and applications more readily maintainable.
Polymorphism offers more flexibility and loose coupling,
allowing code to be readily expanded and maintained over the
course of time.
It can implicitly convert types.
Polymorphism enables the creation of visually appealing software
applications.
11.4: ENCAPSULATION
Encapsulation is among the four key principles to grasp while dealing with
just an Object-Oriented Programming language such as Python. Abstraction,
Polymorphism, and Inheritance are the other three.
11.4.1: Introduction
Giving worldwide access to everyone variables attached to the main
application is not a wise choice whenever operating with classes & handling
sensitive documents. We can access the important variables via
Encapsulation without giving the application complete access to either of
them.
The usage of methods created particularly for the objective may be used
to modify, change, or delete data from variables. Level of flexibility over the
data input & enhanced security is two major advantages of adopting this
modeling approach. Encapsulation can also apply to a technique that prevents
users from accessing anything from an object's variables by preventing
immediate links to certain of its elements. Data variables, data functions, and
data means of a process with an instantiation class/object may be disguised
via Encapsulation.
11.4.2: Encapsulation in Python
Throughout all object-oriented programming languages, Encapsulation is
just the same principle. Whenever the notions are linked to individual
language, the gap becomes noticeable. Python gives worldwide access,
including all elements & methods, unlike languages such as Java, which
allow validated tools (privately or publicly) for variables and methodologies.
Encapsulation is among the most important notions (OOP). Python
encapsulation is indeed the act of bringing together variables that hold data
and analysis that manipulate those variables together into a single entity
known as a class. Variables aren't readily accessible in Python Encapsulation;
instead, they are retrieved via the class's methods. Python Encapsulation
protects the object's internal structure (state and behavior) from the rest of the
program. As a result, python Encapsulation enables data hiding. To grasp the
notion of data concealing, consider this. If a developer outside the class has
access to the information in a variable, there's a good chance they'll write
their own (non-encapsulated) code to handle it. If the solutions aren't
consistent, this would, at the very minimum result in duplicate data (i.e.,
pointless exercise) and incompatibilities.
On the other hand, data hiding implies that everyone must use the same
ways to access data contained in a variable. A class's internal workings and
implementation are not necessary for an application that uses it. The software
merely constructs an object that it then uses to call the class's functions.
Encapsulation Abstraction is a technique for displaying just "necessary"
variables used to access information while hiding implementation specifics
from the client. Imagine your cell phone; everything you need to know is
how to send text messages or talk on the phone. The client does not influence
what transpires until they press a button, how their messages are transmitted,
or how their phone conversations are linked.
Take a look at the sample below to understand the concept
Code:
class Individual:
def __init__(self, name, age=0):
self.name = name
self.age = age
def display(self):
print(self.name)
print(self.age)
individual = Individual('Python', 20)
individual.display()
print(individual.name)
print(individual.age)
Output:

11.4.3: Methods to Control Access


Python presents a range of techniques for preventing access to variables
and actions across a program. With Python encapsulation, access modifiers
are implemented by specifying a class's member functions and procedures as
protected / private. Immediate access modifiers such as public, private, and
protected aren't supported in Python. Single and double underlines may be
used to accomplish this.
Access modifiers restrict a class's variables & functions. The primary
distinctions are private, public, and protected in Python.
Public Member: Can be accessed from any place outside of class.
Private Member: Accessible inside of the Class as just a private member.
Protected Member: Allowed inside the class and its sub-classes.
11.4.4: Public Member
Members containing public data may be accessed both within and outside
a class. This class's member parameters all seem to be public by default.
Code:
class Individual:
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print("Name: ", self.name, 'Age:', self.age)
indv = Individual('Elizabeth', 20)
print("Name: ", indv.name, 'Age:', indv.age)
indv.show()
Output:

11.4.5: Private Member


Variables inside the Class may be made private to preserve access. Put
additional underlines to the beginning of a variable name to classify this as a
private variable. You can't even access private members simply first from
class objects since they're solely accessible inside the class. So the code
throws an error.
Code:
class Individual:
def __init__(self, name, age):
self.name = name
self.__age = age
indv = Individual('Python', 10000)
print('Age:', indv.__age)
Output:

11.4.6: Using Single Underlines


Appending a private variable with only an underline is a standard Python
programming method for identifying it. However, that still doesn't make a
significant difference on the compiler side of the spectrum. As is
conventional, the variable still seems to be available. However, since it is a
programming practice, it informs other developers that the variables or
functions would only be accessed inside the class's range.
Code:
class Individual:
def __init__(self, name, age=0):
self.name = name
self.age = age
def display(self):
print(self.name)
print(self.age)
individual = Individual('Python', 20)
individual.display()
print(individual.name)
print(individual.age)
Output:
11.4.7: Using Double Underlines
One should be using double underlines to declare class members, such as
functions and variables, private. The private modifier may indeed, therefore,
have some implementation in Python. Name mangling is the term for this
process. The class members may still be accessed from outside of the class.
11.4.8: Name Mangling
An identifier containing Var in Python is transformed as Classname Var
via the Python interpreter, yet the class title stays the same. In Python, this
process for modifying identifiers is known as Name Mangling. Python
implies that data access is limited. There were no authentication and key
modifiers. However, Python's Name Mangling may be used to manage
access. Python makes all functions and variables accessible. Every identifier
with two prominent underlines will become a non-public implementation in
Python. To further comprehend Encapsulation, we'll provide non-public
instances variables and functions. The visibility of a non-public instance
method is constrained to its class, and it begins including one or double
underlines, i.e., a solitary "_" or "__" pair in front of a variable or function. A
non-public instances variable's visibility is determined by its class or the
procedure in which it has been declared, and it begins with 2 underlines. If
the two underlines are missing, the methodology is considered public. We
ought to grasp what public and non-public instances variables &
functions before we can appreciate Encapsulation in Python.
An aged variable in a Class individual is altered in the instance below,
and it's preceded by trailing double underlines.
Code:
class Individual:
def __init__(self, name, age=0):
self.name = name
self.age = age
def display(self):
print(self.name)
print(self.age)
individual = Individual('Ali', 10)
individual.display()
print('Trying to use variables that are not in class')
print(individual.name)
print(individual.age)
Output:

11.4.9: Getter and Setter Methods


Accessor (getter) functions and mutators (setter functions) will retrieve
and update private variables because they are class.
Code:
class Individual:
def __init__(self, name, age=0):
self.name = name
self.age = age
def display(self):
print(self.name)
print(self.age)
def getAge(self):
print(self.age)
def setAge(self, age):
self.age = age
individual = Individual('Python', 20)
individual.display()
individual.setAge(25)
individual.getAge()
Output:
11.4.10: Advantages of Encapsulation
Encapsulation improves the flow of data and secures information from
external threats. Encapsulation is a notion that allows code to be self-
contained. It's particularly useful at the implementation level since it
emphasizes the queries while ignoring the complexity. To enable
Encapsulation easier and preserve the information, you must conceal the
information in the component.
Encapsulation aids in the establishment of an improved data flow as well
as data security. Encapsulation is a notion that allows code to be self-
contained. Encapsulation is indeed very beneficial at the operational level
since it concentrates exclusively on the issue "when," abandoning the
difficult "when/where" and accompanying complexity aside. Encasing the
information in a unit makes Encapsulation simpler and more secure.
The most significant benefit of Encapsulation is data security. The
following are some of the advantages of Encapsulation:

Encapsulation prevents customers from stealing information from


an item.
Encapsulation allows employees to access a stage while disclosing
the intricate elements behind it.
It lowers the number of human mistakes.
Makes the software's upkeep easier.
Makes the project more user-friendly.

Object information must nearly always be constrained to private/protected


again for greatest Encapsulation. If you decide to set your appropriate access
to the public, be sure you're aware of the consequences.

Encapsulation permits items to operate independently since the


information is concealed.
Because we can only obtain information concealed in an object
via its methods, it produces connectivity offerings compatible
with the world outside the object.
Objects interact using a layout, which keeps data safe from the
outside world.
If somehow the layout is expected to be the same, all objects may
be replaced immediately.
When Encapsulation wasn't present, small differences in object
action and state might result in system harm.
Encapsulation helps with bug fixes since the system is left
unaffected and the afflicted item is restored.
Encapsulation is extremely effective when rewriting(adding) or
altering a system portion. The complete software does not need to
be edited, implying code efficiency is only feasible via
Encapsulation.
Encapsulation protects the internal features of items while
modifying their external appearance.
Encapsulation of an item is like a network of communication
boxes; if a box gets damaged, the others can be fixed or
substituted without impacting the internal material.

11.4.11: Need for Encapsulation


The following guidelines demonstrate why Encapsulation is useful to
programmers and why the Object-Oriented principle excels in many
programming languages. Each system benefits from Encapsulation since it
allows for well-defined interactions. The Object-Oriented notion in Python is
preoccupied with the reusable rockets of programming. (DRY stands for
Don't Repeat Yourself.) The applications may be kept up to date in a secure
manner. With good code structure, it guarantees the code's adaptability. It
ensures that consumers have a pleasant experience while disclosing any
backend complexity. It increases the code's readability. Any modifications to
one portion of the code will not affect the others. Encapsulation provides data
security and prevents data access by mistake. The techniques outlined above
may be used to access the stored information. In Python, Encapsulation
means that data is concealed outside the object definition. It allows
programmers to create user-friendly experiences. Because the code is very
secure and cannot be viewed by outside sources, it is also useful in
preventing data intrusions.

Every application requires well-defined communication, which


encapsulations assist in attaining.
Object Orientation is a notion. Python programming encourages
the creation of lines of code. DRY (Don't Repeat Yourself) is
another acronym for this.
Application administration is simplified, and security is assured.
Transparency on the programming approach, since developers
should be focused on the class's goal, and complications should be
addressed systematically.
Regular code structure improves program extensibility as well as
Unit Testing.
People found it simple to use the platform as they are sheltered
from the complicated architecture of the backends.
Having all related data in one location & enclosed improves a
component's organization.
Enhances understandability by ensuring that changes in one code
area don't affect other portions.
Encapsulation protects a section of code from being accidentally
visited, but not purposefully since objects contain vital data for
applications that shouldn't be modified anywhere around the code.
11.5: INHERITANCE
One of the essential features of OOP - Object-Oriented Programming is the
concept of inheritance. The most important thing to remember about
inheriting promotes code reusability. As the name implies, inheritance is the
process through which one class acquires the characteristics and methods
from another class. In programming, "Parent class" refers to inherited
attributes and methods. Likewise, the Child class is the class that receives all
of the attributes from its parent class. Instead of rewriting the same code
repeatedly, we can inherit the attributes of one class into another, saving us
time and effort.
Real-world objects are at the heart of object-oriented programming, and
heredity is a method of capturing real-world connections. For example,
consider the following: a vehicle, a bus, and a bicycle are all included in a
bigger category referred to as Vehicle. They have inherited the characteristics
of the vehicle class, i.e., they are all utilized for transportation.
Code:
class Car: #parent class
def __init__(self, name, mileage):
self.name = name
self.mileage = mileage
def description(self):
return f"The {self.name} gives the mileage of {self.mileage}km"
class BMW(Car): #child class
pass
class Audi(Car): #child class
def audi_desc(self):
return
Using the parent class "Car," we've built two child classes, named
"BMW" and "Audi," which have each acquired the methods and attributes of
the parent class. In the BMW class, we have not included any extra features
or techniques that are not already available. On the other hand, Audi has an
extra method found inside the class.
11.5.1: Types of Inheritance
Depending on the number of child and parent classes involved, Python
has four types of inheritance.

Single Inheritance

When a child class becomes a parent class for another child class.
Code:
class Parent:
def ParentFunction1(self):
print("This is parent class 1")
class Child(Parent):
def ChildFunction1(self):
print("This is child class 1")
ob = Child()
ob.ParentFunction1()
ob.ChildFunction1()
Output:

Multiple-Inheritance

It is that a child class inherits from more than one parent class.
Code:
class Parent:
def Parentfunction1(self):
print("This is parent class 1")
class Parent2:
def Parentfunction2(self):
print("This is parent class 2")
class Child(Parent , Parent2):
def ChildFunction1(self):
print("This is child class 1")
ob = Child()
ob.Parentfunction1()
ob.Parentfunction2()
ob.ChildFunction1()
Output:

Multilevel Inheritance

When a child class becomes a parent class for another child class.
Code:
class Parent:
def ParentFunction1(self):
print("This is parent class 1")
class Child(Parent):
def ChildFunction1(self):
print("This is child class 1")
class Child2(Child):
def ChildFunction2(self):
print("This is child class 2")
ob = Child2()
ob.ParentFunction1()
ob. ChildFunction1()
ob. ChildFunction2()
Output:
11.5.2: Parent Class Creation
The parent class is often referred to as the base class. Parent classes
provide a pattern on which children or subclasses may be built, reusing them.
This class may have the capability of creating a child class via inheritance,
eliminating the need to rewrite the same code every time.
As an illustration: To create a bank account, we will use the term "bank
account" as a personal account that contains the child classes "personal
account" and "business account." Several ways between personal and
commercial accounts will be identical, such as ways to withdraw and deposit
money; thus, such methods may be grouped under the parent class of bank
account for convenience.
11.5.3: Child Class Creation
Children are categories that are descended from the base class in some
way. It simply implies that each child's class will utilize the procedures and
variables available in the parent class. For example, business_account is a
child class of bank_account.
Class account(Bank_account)
pass
Remember that the 'pass' keyword is used when you do not add more
attributes or functions to the class.
This step will create a subclass that will inherit the methods and
properties from its parent class.
Instead of passing the 'pass' keyword to the child class, the __int__ ()
function is sent.
Super() allows you to have a child class inherit all of the procedures and
characteristics from its parent class, which is very useful when dealing with
inheritance.
11.5.4: Syntax Inheritance
Inheritance is the ability for a new class category to inherit the attributes
and behaviors of an existing class. The class from which inheritance is
derived is referred to as the parent class. A child class is any class that derives
from a base class in the Java programming language.
Child classes not only inherit all of their parent class's properties and
methods, but they also have the ability to expand or rewrite them.
The following is the standard syntax for class Inheritance:
class Base_Class:
#Base class body
class Derived_Class(Base_Class):
#Derived class body
11.5.5: Advantages of Inheritance

Code reusability; There are many ways available to you that will
allow you to use the same functions and attributes across your
code.
It is a good representation of real-world relationships between
parent and child classes.
In nature, it has a transitive property. If a subclass class gets
attributes from a base class, then all of the child class's subclasses
will likewise receive the characteristics of the parent class, as will
all of the child class's subclasses.
Code reusability is the frequent usage of code that has only been
written once.
A single superclass may represent the number of subclasses within
a chain.
Inheritance eliminates duplication and redundancy in data.
Inheritance is being used to reduce the amount of space and time
required.
There are no modifications to be made in any of the base classes;
only changes need to be made in the parent class.
The ability to pass on inheritance in order to develop more
dominating items.
The use of inheritance forces subclasses to adhere to a common
interface.
Inheritance aids in the reduction of code duplication and the
facilitation of code extension.
The use of inheritance makes it easier to create class libraries.

11.5.6: Disadvantages of Inheritance

Incorrect usage of inheritance, or a lack of understanding of it,


may result in incorrect solutions. In addition, data members in the
base class may be left unused, resulting in memory over-
utilization.
It boosts the time and effort required. Required. Required to go
through the various levels of inheritance.
One of the primary drawbacks of inheriting is the time and effort
required for the program to go through all layers of overloaded
classes and subclasses. A function declared in a particular class
will be executed via each of the 10 different levels of abstraction
above this, which means that it will take ten leaps to run through
the functions written in each of those classes.
The degree of connectivity between a base class and a derived
class is increased via inheritance. A modification to the base class
will have an impact on all of the child classes.
If a method is removed from the "superclass" or collective, we
will have to refactor all of the methods that depend on that method
in the future.
In the case of inheritance, things may become a little tricky since
our program will still build, but the functions of the child class
would no longer be overriding the methods of the superclass.
These approaches will be able to stand on their own as
autonomous techniques in their own right.
The most significant downside of adopting heredity is that the
2 classes (the base class and the inherited class) become
intimately associated with one another. This implies that they
cannot be utilized independently of one another.
Inappropriate use of inheritance may result in incorrect solutions.
Field members in the parent class are often left idle, resulting in
excessive memory use.
Because of the indirection, inherited functions operate slower than
conventional functions.
11.6: MULTIPLE INHERITANCE
As its name is telling, In Python, multiple inheritances occur when a class
derives from numerous classes. In other words, the subclass descends from
more than one base class. An example of this is that a kid acquires
personality qualities from both parents.
The general notion that multiple inheritances seem to be something
"risky" or "bad" is largely perpetuated by computer languages with poorly
designed multiple inheritance capabilities and, above all, by incorrect use of
it. Java doesn't even enable multiple inheritances, but C++ supports it. Python
provides a complex and well-designed solution to multiple inheritances.
If you derive a sub-class from more than one parent class, such a situation
is called Multiple Inheritance. It, however, displays the same behavior, and so
does the single inheritance.
The structure for Multiple Inheritance is likewise comparable to the single
inheritance. By the way, under Multiple Inheritance, the subclass asserts the
methods and properties of all of the parent classes.
In Python, the modules and packages follow a philosophy called DRY,
i.e., don't-repeat-yourself. And class inheriting is a wonderful method to
construct a class utilizing the characteristics of another one and stay DRY.

11.6.1: Syntax for Multiple Inheritance


When creating a class, we put the names of the derived classes within the
parentheses of the names of the parenthesis of the derived classes. This
allows a class to inherit from numerous Python classes. We use commas to
separate the names in this list.
Code:
class Mother:
pass
class Father:
pass
class Child(Mother, Father):
pass
subclass=(Child,Mother)
subclass=(Child,Father)
As a result, the child class inherits attributes from both the mother and
father classes.
11.6.2: Examples for Multiple Inheritance
Consider the following example of multiple inheritance
Code:
class Parentl:
def functionl(self):
print("This is first function 1")
class Parent2:
def function2(self):
print("This is second function 2")
class Parent3:
def function3(self):
print("This is third function 3")
class Child(Parentl, Parent2, Parent3):
def function4(self):
print("This is fourth function 4")
z = Child()
z.functionl()
z.function2()
z.function3()
z.function4()
Output:
Code:
class TeamMember(object):
def __init__(self, name):
self.name = name
class Worker(object):
def __init__(self, pay):
self.pay = pay
class TeamLeader(TeamMember, Worker):
def __init__(self, name, pay, exp):
self.exp = exp
TeamMember.__init__(self, name)
Worker.__init__(self, pay)
print("Name: {}, Pay: {}, Exp: {}".format(self.name, self.pay,
self.exp))

TL = TeamLeader('Jake',5000,'Master')
Output:

11.6.3: Method Resolution Order


When we look for a property in a class that is engaged in multiple
inheritances in Python, a sequence is followed. Firstly, it is checked in the
current class. If it is not identified, the search goes to parent classes. This is a
depth-first, left to right search.
Code:
class A:
def func(self):
print(" This is Parent Class A")
class B(A):
def func(self):
print(" This is Child Class B")
r = B()
r.func()
Output:

This is because the methods called are from class B rather than class A in
the preceding example due to the Method Resolution Order (MRO). The
above-given example is how the classes are listed in the code above: class B
and then class A.
When there are multiple inheritances, the procedures are run in the order
in which they were indicated when the classes were inherited. The sequence
in which procedures are resolved is referred to as the resolution order. Not
important for languages that offer single inheritance, but for languages that
enable multiple inheritances, the order in which methods are resolved is quite
important. This arrangement is referred to as Linearization of a subclass, and
the collection of rules used to achieve it is referred to as MRO (Method
Resolution Order). You may acquire the MRO of a class by using either the
__mro__ property or the mro() method. Both of these methods are available
in Java. In contrast to the __mro__ attribute, the mro() function produces a
python list instead of a tuple.
Method Resolution Order (MRO) is a programming language term that
refers to the order in which a method or property is resolved. The Python
programming language permits classes that derive from other classes. The
class that is being inherited is referred to as the Base or Superclass, whereas
the class that is being inherited is referred to as the Child or Subclass. When a
method is executed in Python, the resolution order specifies the sequence in
which the parent classes are looked for and found. To find the method or
attribute, it must first be searched inside a class, and then it must follow the
inheritance order set while inheriting. This arrangement is also known as
Linearization of a class, and the collection of rules that govern it is known as
MRO (Method Resolution Order). Even though the interpreter is deriving
from another class, it still has to be able to fix the functions that are being
invoked via an instance. As a result, we need the technique resolution order.
11.6.4: Advantages of Multiple Inheritance

Since it contains many base classes, multiple inheritances in C++


enable a derived class to acquire more traits and characteristics
than it would otherwise.
As an added benefit, it ensures transitivity, which means that if
class C inherits from P, then all of the sub-classes of C will
likewise inherit from P.
Because it allows a class to inherit functionality from more than a
base class, it has the benefit of being more flexible.
Designing multiple design patterns has been shown to be useful in
the long run. A few of these patterns are as follows: Pattern for
adapting to some other interface with your class: This pattern is
useful when you need to adapt your class to another interface
using another interface. In this case, Multiple Inheritance plays a
significant part in changing out one interface for another.
Observer patterns include: This pattern is used to keep track of a
list of observers by defining a class for each one.
There are no differences between this derived class and its parent
class.
11.7: OPERATOR OVERLOADING
Python operators can be used with built-in classes, but they don't work with
other types of classes. And the same Operator behaves in different ways with
different types of things. For instance, the + Operator can add two numbers,
combine two lists, or put two strings together. It's called operator
overloading, and it's a feature in Python that lets the same operators have
different meanings based on where they're being used. It is possible for an
installed operator to behave differently depending on the operands we utilize.
This is called operator overloading.
The operator '+' is used to do things like:
1. In this case, add two integers to get a third.
2. Add two strings together.
3. Join two lists together.
Code:
#Join two strings
print("Mahnoor "+"Sadiq")
#Add two numbers
print(7+6)
#Multiply strings
print("Hey"*3)
#Multiply two numbers
print(5*5)
Output:

These operators have been made to work with int and str classes to be
used with both.
11.7.1: Need for Operator Overloading?
Because the compiler doesn't fully understand how well to multiply the
operands, it will give us a compiler error if we try to multiply them with the
Operator *.
Here, let's look at an example of a class we've made ourselves called
Graph. It has two attributes called "X" and "Y." Now, items of this class
would be different points, and adding + to these points (objects) would be an
error.
Code:
class Graph:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Graph(1, 2)
p2 = Graph(2, 3)
print(p1+p2)
Output:

In this case, the compiler doesn't understand what p1+p2 stands for.
We can write a method to override the + Operator to be used differently
to solve this problem.
11.7.2: Preforming Operator Overloading
Python has a special method or magic function automatically called when
that Operator is used to do operator overloading.
For example, when we use the * operator, the magic method __mul__ is
instantly called. This method already has the operation for the * operator set
up.
To make the '+' operator work in a different way, we will use the __add__
magic method to do it.
Code:
class Graph:
def __init__(self, a,b):
self.a = a
self.b=b
def __add__(self, o):
a=self.a+o.a
b=self.b+o.b
ss=Graph(a,b)
return ss
p1=Graph(30,40)
p2=Graph(70,80)
ss=p1+p2
#sum of the a coordinates of graph
print(ss.a)
#sum of the b coordinates of graph
print(ss.b)
Output:

We have added the __add()__ method to the Graph object and called it.
We ended up getting the sum of the coordinates, which was the Graph object.
11.7.3: Overloading Arithmetic Operator
Let us overload the "+" arithmetic operator. To implement the __add__()
method in the class, we will need to overload the + Operator. When you have
enormous power, you also have immense responsibility. Within this function,
we have complete freedom to do anything we want. However, it is more
logical to give a Point object representing the coordinate sum.
Code:
class Coordinates:
def __init__(self, a=0, b=0):
self.a = a
self.b = b
def __str__(self):
return "({0},{1})".format(self.a, self.b)
def __add__(self, other):
a = self.a + other.a
b = self.b + other.b
return Coordinates(a, b)
Now let's try the addition operation again:
Code:
class Coordinates:
def __init__(self, a=0, b=0):
self.a = a
self.b = b
def __str__(self):
return "({0},{1})".format(self.a, self.b)
def __add__(self, other):
a = self.a + other.a
b = self.b + other.b
return Coordinates(a, b)
point1 = Coordinates(10, 20)
point2 = Coordinates(20, 30)
print(point1+point2)
Output:

Essentially, when you combine points 1 and 2 in a statement, Python calls


point1. __add (point2) returns Coordinates, which in turn returns
Coordinates.__add__(point1,point2). Following that, the addition operation is
performed in the manner that we stated.
11.7.4: Overloading Comparison Operator
Python does not restrict the use of operator overloading to simply
arithmetic operators. It is also possible to overload comparison operators.
Consider the scenario in which we wished to include the less than symbol in
our Point class. To do this, let us compare the magnitudes of these points as
they are measured from the origin and report the result. It is possible to apply
it in the following ways.
Code:
class Coordinates:
def __init__(self, a=0, b=0):
self.a = a
self.b = b
def __str__(self):
return "({0},{1})".format(self.a, self.b)
def __lt__(self, other):
self_mag = (self.a ** 2) + (self.b ** 2)
other_mag = (other.a ** 2) + (other.b ** 2)
return self_mag < other_mag
point1 = Coordinates(1,1)
point2 = Coordinates(-2,-3)
point3 = Coordinates(1,-1)
print(point1<point2)
print(point2<point3)
print(point1<point3)
Output:

11.7.5: List of Operators Overloaded in Python


List of Arithmetic Operators overloaded in Python:

Addition: p1+p2
Subtraction: p1-p2
Multiplication: p1*p2
Power: p1 ** p2
Division: p1/p2
Floor Division: p1//p2
Remainder: p1%p2
Bitwise NOT: ~p1
Bitwise XOR: p1^p2
Bitwise OR: p1|p2
Bitwise AND: p1&p2
Bitwise Left Shift: p1<<p2
Bitwise Right Shift: p1>>p2

List of Comparison Operators overloaded in Python:

Less than: p1<p2


Less than or equal to: p1<=p2
Equal to: p1==p2
Not equal to p1!= p2
Greater than p1>p2
Greater than or equal to p1>=p2

11.7.6: Advantages of Operator Overloading

It allows us to reuse things instead of writing a lot of different


methods that aren't very similar. We can try writing one method
and then overload it.
Besides, it also helps make the code easier to understand and write
down and reduces the amount of work.
They offer the same support as built-in types for user-defined
types.
It's easier to understand a program when operators are changed to
do different things.
Operator overloading allows programmers to use notation similar
to the target field.
It improves the readability and understandability of the code.
CHAPTER 12
DATA STRUCTURES AND
ALGORITHMS

D ata Structure is a method for gathering and classifying the data in such a
manner that actions on them may be performed efficiently. Data
Structures is the process of representing data pieces in terms of a connection
in order to facilitate organization and storage. For instance, we include some
data indicating the client's name is "Eras," and her age is 19. Here, "Eras" is a
String, and 19 is an integer.
We can arrange this data by creating a record called Client, which will
include both the clients' names and ages. Now, as a data structure, we may
gather and store clients' records in a database file. For instance, "Daryl" is 10,
"Joline" is 11, and "Lennie" is 13.
If you are accustomed to Object-Oriented programming standards, a class
does the same thing; it aggregates several types of data into a single entity.
The main distinction is that data structures include ways for effectively
accessing and manipulating data.
In basic terms, Data Frameworks are built to hold ordered data in a
manner that facilitates the execution of different operations on it. It is a
representation of the knowledge associated with the material to be arranged
in memory. It ought to be designed and executed in such an approach that
complexity is minimized and efficiency is maximized.
12.1: ASYMPTOTIC ANALYSIS
An asymptotic analysis of a procedure is a way to figure out the math behind
how well it works at running. Asymptotic analysis can help us figure out the
best case, the average case, and the worst case of an algorithm.
This type of analysis is called "input bound," which means that if there is
no input to the method, it is thought that it will work in an endless amount of
time. Other than "input," all other components are recognized to be the same.
Asymptotic analysis is the process of figuring out how long an operation
will take in terms of mathematic units of time. For example, for one
operation, the running time is f(n), and for another, it is g(n) (n2). As the
amount of operations grows, so does the first operation's running time. The
second operation's running time will grow exponentially as the number of
operations increases. Both operations will run for about the same amount of
time if n is very small.
Most of the time, an algorithm takes falls into three groups. −
Best Case: This is when the program runs in the shortest amount of time.
Average Case: The average time it takes to run a program.
Worst Case: The longest time it will take to run the program.
Asymptotic notations are used to figure out how long an algorithm takes
to run. These are the most common ones.

1. θ Notations – Best Case


2. Ω Notations – Average Case
3. O Notations – Worst Case

Theta (θ) Notation


The notation (n) is the correct way to say both the relatively low bound
and the higher bound of an application's runtime in a correct way. It is shown
in this way:
Omega (Ω) Notation
The notation Ω(n) is the official way to phrase that an algorithm's runtime
can't go below a certain amount of time. Time complexity is a way to figure
out how long an algorithm could take to finish in the best case.

Big Oh (O) Notation


People say O(n) to show that an algorithm's runtime can't go over a
certain number of steps. It quantifies the worst-case time complexity or how
long an algorithm could take to finish.
General Notations

constant
Ο(1)
polynomial
n Ο(1)
logarithmic
Ο(log n)
linear
Ο(n)
exponential
2 Ο(n)
quadratic
Ο(n 2)
cubic
Ο(n 3)
n log n
Ο(n log n)
12.2: LINEAR DATA STRUCTURES
In this section, the linear type of data structures like stacks, queues, and so on
are discussed.
12.2.2: Stack
A linear data structure that uses the First-In and Last-Out (FILO) or Last-
In and First-Out (LIFO) ordering to store objects. A new mechanism is added
to one end of a stack, while an element is withdrawn from the other end.
Insert and delete processes are sometimes referred to as push and pop.
Stack is connected with the following functions:

push(x) – this inserts an element at the top – Complexity = O(1)


empty() – determines if the stack is empty – Complexity = O(1)
top() – determines the element at the top of stack – Complexity =
O(1)
size() – determines the size – Complexity = O(1)
pop() – deletes the top element – Complexity = O(1)

Implementation
In Python, a stack may be implemented in a variety of ways. This article
discusses how to create a stack using Python data structures and modules.
In Python, the stack may be implemented in the following ways:

List
queue.LifoQueue
Collections.deque

Implementation of LifoQueue using a list


The list data structure included with Python may be utilized as a stack.
Instead of pushing items to the top of the stack, append() adds them to the top
of the stack, whereas pop() removes them in LIFO order.
Regrettably, the list contains a few inaccuracies. The primary
disadvantage is that it may encounter speed challenges as it expands. Because
the list elements are kept next to one another in memory, if the stack becomes
larger than the block of memory presently holding it, Python must perform
memory allocations. As a result, some add() calls may take much longer than
others.
Code:
stack = [] #assign stack a list
stack.append('1') #storing 1 as string
stack.append('2') #storing 2 as a string
stack.append('3') #storing 3 as a string
print('Initial stack')
print(stack) #display the stack elements
print('\nElements popped:') #the elements popped are displayed by
print(stack.pop()) #stack.pop() pops top element and print() displayed
popped element
print(stack.pop()) #new element at top of stack is popped
print(stack.pop()) #another popped
print('\nNew stack after elements popped:')
print(stack)
#as only 3 elements were initially in stack, and 3 were popped, the stack
is now empty
Output:

Implementation of Stack using Collections


Collections.deque are used to implement stack using deque class from the
collections package may be used to construct a Python stack. Deque is
favored over the list in circumstances when faster append and pop operations
are required from both ends of the container since deque has an O(1)
computation time for append and pop operations, while the list has an O(n)
time complexity.
The deque's append() and pop() functions are identical to those in the list
().
Code:
from collections import deque
stack = deque()
stack.append('1')
stack.append('2')
stack.append('3')
print('Stack:')
print(stack)
print('\nElements popped:')
print(stack.pop())
print(stack.pop())
print(stack.pop())
print('\nStack after popped:')
print(stack)
Output:

Implementation through Queue Module


Additionally, the Queue module includes a Queue LIFO, which is
essentially a Stack. The put() method is used to input data into the Queue,
while the get() function is used to get data from the Queue.
This module contains the following functions:

get() – This removes and returns an element from the Queue. If it


is empty, then it waits until an element exists.
Put (element) – This appends an element into the Queue. If it's
full, then it waits until it is not.
Full () – This tells the user whether the Queue is full or not by
returning true if the maximum number of elements allowed are
used.
empty() – This returns true if there is a stack underflow (when
empty, it is considered underflow)
get_nowait() – This returns an element if available otherwise, it
determines Queue empty.
put_nowait() – This puts an item to the Queue immediately if
space is available, else returns stack overflow (when no space
available)
qsize() – This returns the number of elements in the Queue.

Code:
from queue import LifoQueue
stack = LifoQueue(maxsize=4) #initialize stack at 4
print(stack.qsize()) #display size
stack.put('1')#push onto stack
stack.put('2')
stack.put('3')
stack.put('4')
print("Full Stack: ", stack.full())
print("Size Stack: ", stack.qsize())
print('\nElements popped from the stack')
print(stack.get())#LIFO order get top and pop
print(stack.get())
print(stack.get())
#now one remaining
print("\nNon empty: ", stack.empty())
Output:

12.2.3: Queues
Like a stack, a queue is a sequential data model that holds items in a
(FIFO) First In First Out order, just like the stack. To get rid of something
from a queue, start with the item that was last added. Queue: Any line of
people waiting to get something where the person who came first has
functioned first.
Operations Used:

Enqueue: Appends an element to the Queue. If the Queue is


packed, then it's considered as an Overflow form – Complexity =
O(1)
Dequeue: Withdraws an element from the Queue. They pop in the
very same order they were being pushed. If the Queue is clear,
then it's considered as an Underflow form – Complexity = O(1)
Front: Retrieves top item – Complexity = O(1)
Rear: Retrieves last item – Complexity = O(1)

There are many ways to make a queue in a Programming language. This


article talks about how to make a queue with data structures and components
from the Python library.
How to make a queue in Python:

1. queue.Queue
2. collections.deque
3. list

Queue Implementation
As described in Section' 12.2.2: Stack Implementation through
Queues', the implementation of queues using:
from queue import Queue
; is the same as described in section 12.2.2.
Collections Implementation
The deque class through the collection modules may be used to create a
queue in Python. When we require faster append and pop actions from both
ends of a container, the deque is chosen over the list because deque has an
O(1) computation time for add and remove operations, whereas the list has an
O(n) time complexity. The operations append(), and popleft() is used rather
than enqueue and deque.
Code:
from collections import deque #imports collections through deque
q = deque() #initializes queue through deque
# Adding elements 1,2,3 to queue (enqueue)
q.append('1')
q.append('2')
q.append('3')
print("Queue at start: ")
print(q)
# Removing elements from a queue
print("\nelements popped: ")
print(q.popleft())
print(q.popleft())
print(q.popleft())
print("\nUpdated Queue: ")
print(q)
#if we use q.popleft() now, it will raise an index error as list is now empty

Output:

Lists Implementation
The list is a built-in data structure in Python that can be utilized as a
queue, and it is called a list. Rather than enqueue() and dequeue(), the
append() and pop() functions are used to add and remove items from a queue,
instead. Lists, on the other hand, are very slow for this because it takes a lot
of time to add or remove an attribute at the beginning.
Code:
# Initializing
queue = [] #queue as list
# enqueue 1,2,3
queue.append('1')
queue.append('2')
queue.append('3')
print("Queue 1: ")
print(queue)
# Removing elements from the queue from head, (FIFO)
print("\nElements dequeued: ")
print(queue.pop(0))
print(queue.pop(0))
print(queue.pop(0))
print("\nQueue after removal")
print(queue) #queue now empty
Output:

12.2.4: Linked Lists


A linked list is created by nodes, each of which has a data field and a
link towards the next node in the linked list.

There are a number of types of linked lists that can be used, the one
shown over is known as a singly linked list.
For example, suppose we have a sorted collection of IDs in an array
called list1[].
List1[] = [2000, 2020, 2050,3000, 3040]
And because you want to add a new element of List1 2005, we'll have to
shift all the components after 1000 to keep the sorted order (excluding 2000).
Code:
# Node class defined
class Node:
def __init__(self, data):
self.Nodedata = data
self.nextNode = None
# next is initialized as none
class LinkedList: #linked list class
def __init__(self):
self.Listhead = None
Output:
No output as this is only creating classes, not objects, nor is it returning
anything.
You can create a linked list object by the following:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
# Main Code Begins:
if __name__=='__main__':
# Create object of empty list
list1 = LinkedList()
list1.head = Node(1)
second = Node(2)
third = Node(3)

'''
Three nodes have been created. But is not yet a linked list because the
nodes aren't linked
References to three blocks as defined as head, second and third
'''
list1.head.next = second; # this linked first head to second node
second.next = third; # Link second node to third
Output:
This also shows no output but creates a linked list of 3 nodes.
Traversal
We generated a basic linked list with 3 nodes in the previous program.
Let's go over the list and display the info for each node. Let's create a
generalized printList() that displays any provided list for traversal.
Code:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def display(self): #Here add the display function
temp = self.head #initialize head as the temp node
while (temp): #while the node temp exists (is not none)
print (temp.data) #display data of node
temp = temp.next #move to next node
if __name__=='__main__':
list1 = LinkedList() #object created
list1.head = Node(1)
second = Node(2)
third = Node(3)
list1.head.next = second; #link
second.next = third; #link
list1.display() #call display method
Output:

Insertion of a New Node


The various ways for adding a new node to a linked list are explored.
There are three ways to add a node.

1. The first item in the linked list

Code:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
# Function to initialize the Linked List object
def __init__(self):
self.head = None
def display(self): #display function
temp = self.head
while (temp):
print (temp.data)
temp = temp.next
def push(self, new_data): #insert add
new_node = Node(new_data) #add the new data in a new node
new_node.next = self.head #link the next pointer of new node to
current head
self.head = new_node #make the new node the new head
linkedL=LinkedList()
linkedL.push(5)
linkedL.push(3)
linkedL.push(1)
linkedL.display()

Output:

1. Following a certain node:


Code:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
# Function to initialize the Linked List object
def __init__(self):
self.head = None
def display(self): #display function
temp = self.head
while (temp):
print (temp.data)
temp = temp.next
def push(self, new_data): #insert at head
new_node = Node(new_data) #add the new data in a new node
new_node.next = self.head #link the next pointer of new node to
current head
self.head = new_node #make the new node the new head
def insertAfter(self, prev_nodedata, new_data): #insert at any given
position
temp=self.head #store the head at temp
prevnode=None
while (temp): #while node exists
if temp.data==prev_nodedata: #find the node at which new node
must come
prevnode=temp #once found, store at prevnode
temp=temp.next#traverse
if prevnode==None:
print("This element does not exist in linked list.") #if not found
then it doesnt exist
new_node = Node(new_data) #once found, create a new node
new_node.next = prevnode.next #link the nodes
prevnode.next = new_node #store node

linkedL=LinkedList()
linkedL.push(5)
linkedL.push(3)
linkedL.push(1)
linkedL.insertAfter(3,9)#insert 9 after 3
linkedL.display()
Output:

1. At the conclusion

Code:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
# Function to initialize the Linked List object
def __init__(self):
self.head = None
def display(self): #display function
temp = self.head
while (temp):
print (temp.data)
temp = temp.next
def push(self, new_data): #insert at head
new_node = Node(new_data) #add the new data in a new node
new_node.next = self.head #link the next pointer of new node to
current head
self.head = new_node #make the new node the new head
# This function is explained in Linked List class definition
# Appends a new element at the end of the list. This operation is
# defined inside the LinkedList class displayed above */
def append(self, new_data):
new_node = Node(new_data)
if self.head is None:
self.head = new_node
return
last = self.head
while (last.next):
last = last.next
last.next = new_node
linkedL=LinkedList()
linkedL.push(5)
linkedL.push(3)
linkedL.push(1)
linkedL.append(0)#insert 9 at the end
linkedL.display()
Output:

Deletion of a Node
To comprehend the deletion process, let's develop a problem statement:
Remove the first instance of a 'key.'
Assume a linked list with a small number of entries. Our goal is to create
a function that removes the specified node from the collection. So, if the list
starts with 1 3 5 7 9, after eliminating 3, it will be 1 5 7 9.
Consider the pointer 'node,' which points to the node to be destroyed. To
delete the node, we must do the following procedures.

node.next.val = node.val
node.next.next = node.next.next

Code:
class node:#create node class
def __init__(self, data, next = None):
self.value = data
self.nextPtr = next
#function make a list
def make(nodes):
head = node(nodes[0])
for nodes in nodes[1:]:
ptr = head
while ptr.nextPtr:
ptr = ptr.nextPtr
ptr.nextPtr = node(nodes)
return head
#traverse list
def printL(head):
ptr = head
print('Linked List: [', end = "")
while ptr:
print(ptr.value, end = ", ")
ptr = ptr.nextPtr
print(']')
class deletionclass(object):#class for deletion
def deleteNode(self, node, data):#void type function
while node.value is not data:
node = node.nextPtr
node.value = node.nextPtr.value
node.nextPtr = node.nextPtr.nextPtr
LL = make([0,34,1,4,9,10])#pass 0, 34, 1, 4, 9, 10 as a linked list
printL(LL) #traverse initial list
delHere = deletionclass() #create object for deletionclass called delHere
delHere.deleteNode(LL, 4) #pass the linked list LL and the value to
delete, 4, to a method deleteNode
printL(LL) #traverse new list
Output:

Search for a Given Value


When a user enters any value given linked list, search for that value and
return true if it is existing, else return false.
Code:
class Node:
def __init__(self, data):
self.val = data
self.nextPtr = None
class LinkedList:
def __init__(self):
self.head = None
def push(self, new_data):
new_node = Node(new_data)
new_node.nextPtr = self.head
self.head = new_node
def search(self, searching):#searching method
curr = self.head#temp node
while curr != None:#while node exists
if curr.val == searching:#if in case current nodes data is equal to
the data user wants
return True #exit the function with true
curr = curr.nextPtr#else traverse while curr exists
return False #if no node, then return false
if __name__ == '__main__':#MAIN function
llSearch = LinkedList()
llSearch.push(0);#0,34,1,4,9
llSearch.push(34);
llSearch.push(1);
llSearch.push(4);
llSearch.push(9);
if llSearch.search(4):#Search for 4 and return true
print("Yes. IT EXISTS")
else:
print("No. IT DOESNT EXIST")#else false
Output:
12.3: NON-LINEAR DATA STRUCTURES
Non-linear data structures like trees are explained.
12.3.1: Trees
This is a tree where every single member has no more than two offspring.
Because each element may only have two children, they are commonly stated
as the left and right child nodes.
Components:

Data
Pointer to the right child
Pointer to left child

The root of the tree is the highest node. The components just underneath
an element are referred to as its offspring. Its parent is the element that sits
exactly above it. For instance, 'a' is a child of 'f,' and 'f' is 'a's' parent. Finally,
no-child components are referred to as leaves.
Tree Class Code:
class Node:
def __init__(self,key):
self.left = None #left node
self.right = None #right node
self.val = key
root = Node(2) #create object
root.left = Node(9);
root.right = Node(1);
root.left.left = Node(4);
''' 2
/ \
9 1
/ \ / \
4 None None None
/ \
None None'''
Types of Binary Trees
Full Binary
Binary Tree in its Full Form, if each node has 0 or 2 offspring, it is called
a complete binary tree. A whole binary tree is shown in the examples below,
where all nodes have two offspring except the leaf nodes.
Code:
class Node:
def __init__(self, item):
self.item = item
self.leftnode = None
self.rightnode = None
def FullTree(root):
# Tree in empty case
if root is None:
return True
# Check if child present
if root.leftnode is None and root.rightnode is None:
return True
if root.leftnode is not None and root.rightnode is not None:
#recursively call to check for every node
return (FullTree(root.leftnode) and FullTree(root.rightnode))
return False
root = Node(2) #creating tree
root.rightChild = Node(4)
root.leftChild = Node(3)
root.leftChild.leftChild = Node(5)
root.leftChild.rightChild = Node(6)
root.leftChild.rightChild.leftChild = Node(7)
root.leftChild.rightChild.rightChild = Node(8)
if FullTree(root): #if true returned
print("tree is a full binary tree")
else: #if false returned
print("tree is not full binary tree")
Output:

Complete Binary
When each and every level is entirely filled, with the exception of the
lowest, which is possibly filled from the left, it is similar to a full binary tree,
but there are two significant distinctions.

A complete binary tree is not always a full binary tree because the
last leaf element may not have the right sibling.
The leaf components must all slant to the left.
Code:
# Checking if complete
class Node:

def __init__(self, item):


self.item = item
self.left = None
self.right = None
def count(root): #count the nodes
if root is None:
return 0
return (1 + count(root.left) + count(root.right))#recursive call to count
# Check if complete
def complete(root, index, numberNodes): #pass parameters root index
and number of nodes in tree
# in case tree is empty
if root is None:
return True
if index >= numberNodes: #if the index is greater than total number of
nodes
return False
return (complete(root.left, 2 * index + 1, numberNodes)#otherwise
return recursive call
and complete(root.right, 2 * index + 2, numberNodes))

root = Node(2) #create tree


root.left = Node(3)
root.right = Node(4)
root.left.left = Node(5)
root.left.right = Node(6)
root.right.left = Node(7)
node_count = count(root)
index = 0
if complete(root, index, node_count):
print("The tree complete binary tree")
else:
print("The tree not a complete binary tree")
Output:

Perfect Binary
A perfect binary tree is one in which each internal node has precisely two
child nodes, and all leaf nodes would be at the same level. A degree of 2 is
assigned to all internal nodes.
A perfect binary tree can be accurate and useful information as recursion:

1. height h = 0 is formed when a single node has no offspring.


2. If both of a node's subtrees are also of height h - 1 and are non-
overlapping.
Perfect Binary Tree Theorems

height h has 2 h + 1 – 1 node.


The average depth of a node is Θ(ln(n)).
n nodes have height log(n + 1) – 1 = Θ(ln(n)).
height h has 2 h leaf nodes.

Code:
# if a binary tree is perfect
class newNode:
def __init__(self, k):
self.key = k
self.right = self.left = None
# To calculate depth
def calcDepth(node):
a=0
while (node is not None):
a += 1
node = node.left
return a
# Check if perfect binary tree
def perfect(root, d, level=0):
# in empty tree case
if (root is None):
return True
# presence of trees
if (root.left is None and root.right is None):
return (d == level + 1)
if (root.left is None or root.right is None):
return False
return (perfect(root.left, d, level + 1) and
perfect(root.right, d, level + 1))
root = None
root = newNode(2)
root.left = newNode(3)
root.right = newNode(4)
root.left.left = newNode(5)
root.left.right = newNode(6)
if (perfect(root, calcDepth(root))):
print("The tree is perfect binary tree")
else:
print("The tree not perfect")
Output:

Balanced Binary
The height is balanced if it is O(log n), where n is the total number of
nodes. The AVL tree, for example, maintains O(log n) height by ensuring
that the height difference between the left and right subtrees is no more than
1. Red-Black trees keep their height constant by ensuring that the proportion
of nodes on each root-to-leaf path is the same and that no neighboring red
nodes exist. Even Binary Search trees provide high performance since they
search, insert, and delete in O(log n) time.
The criteria for a height-balanced binary tree are as follows:

The left subtree is balanced


The other subtree is balanced.
If there is no more than one variation between the left and right
subtrees for every node
Code:
# Checking if balance
class Node:
def __init__(self, data):
self.data = data
self.left = self.right = None
class Height: #class for height checking
def __init__(self):
self.height = 0
def HeightBalanced(root, height):
left_height = Height()#height for both sides of tree
right_height = Height()
if root is None:#if empty
return True
left = HeightBalanced(root.left, left_height)#recursively call to find
right = HeightBalanced(root.right, right_height)
height.height = max(left_height.height, right_height.height) + 1
#increment node wise
if abs(left_height.height - right_height.height) <= 1:
return left and right
return False
height = Height()#make object of height
root = Node(2)#create tree
root.left = Node(3)
root.right = Node(4)
root.left.left = Node(5)
root.left.right = Node(6)
if HeightBalanced(root, height): #if true
print('is balanced')
else:
print('not balanced')
Output:

Search Binary Tree


A binary search tree data structure that allows us to keep a sorted list of
integers in a short amount of time. Because each tree joint can only have two
offspring, it's termed a binary tree. It can be used to check for the presence of
a large variety in O(log(n)) time.
The physical qualities that differentiate a binary search tree from a typical
binary tree are as observed:

The left subtree's nodes are all smaller than the root node.
The root node is greater than all nodes in the right subtree.
Each node's subtrees are likewise BSTs, the same two attributes as
the node.
To demonstrate that a tree with a right subtree for one value smaller than
the root is not a legitimate binary search tree, a tree with a right subtree for
one value smaller than the root is displayed.
Operation of Search
The approach is based on the BST characteristic that each left subtree has
values lower than the root, and each right subtree has data higher than the
root.
If the number is below the root, we can be confident it is not in the right
subtree; we only need to look in the left subtree, and if the value is over the
root, we can be sure it is not in the left subtree; we only need to look in the
right subtree.
Algorithm Used:
If root == NULL
return NULL;
If number == root->data
return root->data;
If number < root->data
return search(root->left)
If number > root->data
return search(root->right)
Insert Node
The goal is to use a queue to iteratively traverse the supplied tree in level
order. If a node's left child is null, we create a new key as the node's left
child. If the right child of a node is empty, we find the new key the right
child. We continue to traverse the tree until we locate a node with an empty
left or right child.
Code:
# program to inject an element in the binary tree
class newNode():
def __init__(self, data):
self.key = data
self.left = None
self.right = None
#Inorder traversal binary tree
def inorder(temp):
if (not temp):
return
inorder(temp.left)
print(temp.key,end = " ")
inorder(temp.right)
#insertion of new node
def insert(temp,key):
if not temp:
root = newNode(key)
return
q = []
q.append(temp)
while (len(q)):
temp = q[0]
q.pop(0)
if (not temp.left):
temp.left = newNode(key)
break
else:
q.append(temp.left)
if (not temp.right):
temp.right = newNode(key)
break
else:
q.append(temp.right)
# Driver code
if __name__ == '__main__':
root = newNode(11)
root.left = newNode(12)
root.left.left = newNode(8)
root.right = newNode(10)
root.right.left = newNode(16)
root.right.right = newNode(9)
print("Traversal before insertion:", end = " ")
inorder(root)
key = 12
insert(root, key)
print()
print("Traversal after insertion:", end = " ")
inorder(root)
Output:

Delete a Node
Remove a node from a binary tree by ensuring that the tree declines from
the bottom up (i.e., the deleted node is replaced by the leaf and rightmost
node). This is not the same as removing BST. Because there is no order
among the items, in this case, we replace it with the last item.

1. Begin at the root and locate the lowest and rightmost element in
the binary tree, as well as the node that we wish to remove.
2. Replace the data of the node to be eliminated with the data of the
deepest rightmost node.
3. Delete the lowest rightmost node after that.

Code:
class Node:
def __init__(self,data):
self.data = data
self.left = None
self.right = None
def inorder(temp):
if(not temp):
return
inorder(temp.left)
print(temp.data, end = " ")
inorder(temp.right)
def deleteDeepest(root,d_node): #delete deepest node
q = [] #create empty list
q.append(root) #append root
while(len(q)): #until end of list
temp = q.pop(0)
if temp is d_node:
temp = None
return
if temp.right:
if temp.right is d_node:
temp.right = None
return
else:
q.append(temp.right)
if temp.left:
if temp.left is d_node:
temp.left = None
return
else:
q.append(temp.left)
#deletion of node
def deletion(root, key):
if root == None :
return None
if root.left == None and root.right == None:
if root.key == key :
return None
else :
return root
key_node = None
q = []
q.append(root)
temp = None
while(len(q)):
temp = q.pop(0)
if temp.data == key:
key_node = temp
if temp.left:
q.append(temp.left)
if temp.right:
q.append(temp.right)
if key_node :
x = temp.data
deleteDeepest(root,temp)
key_node.data = x
return root
# Driver code
if __name__=='__main__':
root = Node(11)
root.left = Node(12)
root.left.left = Node(8)
root.left.right = Node(13)
root.right = Node(10)
root.right.left = Node(16)
root.right.right = Node(9)
print("before the deletion:")
inorder(root)
key = 12
root = deletion(root, key)
print()
print("after the deletion:")
inorder(root)
Output:
If the entity to be removed is the innermost node itself, shown above code
will not work, so after the task deletDeepest(root, temp) accomplishes
operation, the key node is deleted (as the key node is equal to temp), and then
replacing key node's data with the deepest node's data(temp's data) tosses a
runtime error.
CHAPTER 13
PYTHON GAMING PROJECT
13.1: MARIO GAME IN PYTHON WITH SOURCE
CODE
T his Mario Game is created in Python interpreter. This Game Code is
patterned in Graphical User Interface that works with the
PyGame library. Telling regarding the gameplay, it is a one-player Python
game, where the participant (Mario) has to escape fireballs getting out from
the flying dragon. Every level arrives with more troubles. The region gets
smaller & smaller as early as there is a rise in level. In this great Mario
Python lesson, you could discover how to create a great Mario video game in
Python.
This Mario video game program in Python easy & clean Graphical User
Interface is offered for simple gameplay. The gameplay layout is so minimal
that the user will not discover it hard to use & understand. Various images are
utilized in the advancement of this video game design. The gaming
atmosphere is just like the Mario video game.
Anyhow if you like to rank up your expertise in programming,
particularly video games in Python, try this project.
To begin creating this Mario video game in Python, be sure that you
got PyCharm IDE downloaded on your pc.
13.2: STAGES ON HOW TO MAKE A MARIO VIDEO
GAME IN PYTHON
Phase 1: Create your project name.
Initially, open PyCharm IDE & then create your “project name” after
creating the project name, tap on the “create” icon.

Phase 2: Create your Python file.


Next, after creating your project name, “right tap” the project name &
then tap on “new” after that, tap on the “python file. “
Step 3: Name the python file.

Next, after making a python file, Name the Python file after clicking “enter. “

Phase 4: The real code.


You can now copy the code elaborated with the comments. It will be easy
for you to understand the depth of the code and understanding logic.
13.3: CODE FOR IMPORTING LIBRARIES
import pygame #importing pygame library
import sys #impoting system library
13.4: CODE FOR THE INITIALIZATION &
DECLARATION OF FUNCTIONS
pygame.init() #initialization
WINDOW_WIDTH = 1200 #setting for window
WINDOW_HEIGHT = 600
FPS = 20
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
ADD_NEW_FLAME_RATE = 25
cactus_img = pygame.image.load('cactus_bricks.png')
cactus_img_rect = cactus_img.get_rect()
cactus_img_rect.left = 0
fire_img = pygame.image.load('fire_bricks.png')
fire_img_rect = fire_img.get_rect()
fire_img_rect.left = 0
CLOCK = pygame.time.Clock()
font = pygame.font.SysFont('forte', 20)
canvas = pygame.display.set_mode((WINDOW_WIDTH,
WINDOW_HEIGHT))
pygame.display.set_caption('Mario')
class Top_score: #creating top score class
def __init__(self): #constructor initialized
self.high_score = 0 #initializing with 0
def topscore_(self, score): #fuction defined
if score &gt; self.high_score:
self.high_score = score
return self.high_score #returned
tp = Top_score() #object created
class Dra_gon: #class created
dragon_velocity = 10
def __init__(self):
self.dragon_img = pygame.image.load('dragon.png') #getting
images
self.dragon_img_rect = self.dragon_img.get_rect()
self.dragon_img_rect.width -= 10
self.dragon_img_rect.height -= 10
self.dragon_img_rect.top = WINDOW_HEIGHT/2
self.dragon_img_rect.right = WINDOW_WIDTH
self.up = True
self.down = False
def edit(self): #function defined
canvas.blit(self.dragon_img, self.dragon_img_rect)
if self.dragon_img_rect.top &lt;= cactus_img_rect.bottom: #if else
statement used
self.up = False
self.down = True
elif self.dragon_img_rect.bottom &gt;= fire_img_rect.top:
self.up = True
self.down = False
if self.up:
self.dragon_img_rect.top -= self.dragon_velocity
elif self.down:
self.dragon_img_rect.top += self.dragon_velocity
class Flames_: #class created again
flames_velocity = 20
def __init__(self): #constructor
self.flames = pygame.image.load('fireball.png') #getting images
self.flames_img = pygame.transform.scale(self.flames, (20, 20))
self.flames_img_rect = self.flames_img.get_rect()
self.flames_img_rect.right = dragon.dragon_img_rect.left
self.flames_img_rect.top = dragon.dragon_img_rect.top + 30
def edit(self): #function called
canvas.blit(self.flames_img, self.flames_img_rect) #setting images
if self.flames_img_rect.left &gt; 0:
self.flames_img_rect.left -= self.flames_velocity
class Mario_: #class created of Mario
velocity = 10
def __init__(self): #constructor called
self.mario_img = pygame.image.load('maryo.png') #getting images
self.mario_img_rect = self.mario_img.get_rect()
self.mario_img_rect.left = 20
self.mario_img_rect.top = WINDOW_HEIGHT/2 - 100
self.down = True
self.up = False
def edit(self): #fuction defined
canvas.blit(self.mario_img, self.mario_img_rect)
if self.mario_img_rect.top &lt;= cactus_img_rect.bottom:
gameover() #function called
if SCORE &gt; self.mario_score:
self.mario_score = SCORE
if self.mario_img_rect.bottom &gt;= fire_img_rect.top:
gameover() #function called
if SCORE &gt; self.mario_score:
self.mario_score = SCORE
if self.up:
self.mario_img_rect.top -= 10
if self.down:
self.mario_img_rect.bottom += 10
def gameover(): function defined
pygame.mixer.music.stop()
music = pygame.mixer.Sound('mario_dies.wav')
music.play()
tp.topscore_(SCORE) #function called with object name
game_over_img = pygame.image.load('end.png')
game_over_img_rect = game_over_img.get_rect()
game_over_img_rect.center = (WINDOW_WIDTH/2,
WINDOW_HEIGHT/2)
canvas.blit(game_over_img, game_over_img_rect)
while True: #loop forever
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
music.stop()
game_loop()
pygame.display.update()
13.5: CODE FOR THE GAME STARTING
def start_the_game(): #start function
canvas.fill(BLACK)
start_img = pygame.image.load('start.png') #adding images
start_img_rect = start_img.get_rect()
start_img_rect.center = (WINDOW_WIDTH/2,
WINDOW_HEIGHT/2)
canvas.blit(start_img, start_img_rect)
while True: #loop forever
for event in pygame.event.get(): #for loop used
if event.type == pygame.QUIT: #use of if else statement
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
gameloop() #function called
pygame.display.update()
13.6: CODE FOR THE GAME LEVEL
global LEVEL #global variable
if SCORE in range(0, 10):
cactus_img_rect.bottom = 50
fire_img_rect.top = WINDOW_HEIGHT - 50
LEVEL = 1
elif SCORE in range(10, 20):
cactus_img_rect.bottom = 100
fire_img_rect.top = WINDOW_HEIGHT - 100
LEVEL = 2
elif SCORE in range(20, 30):
cactus_img_rect.bottom = 150
fire_img_rect.top = WINDOW_HEIGHT - 150
LEVEL = 3
elif SCORE &gt; 30:
cactus_img_rect.bottom = 200
fire_img_rect.top = WINDOW_HEIGHT - 200
LEVEL = 4
13.7: CODE FOR THE GAME MAIN MODULE
def gameloop(): #funtion defined
while True: #loop forever
global dragon
dragon = Dra_gon() #object created
flame = Flames_() #object created
mario = Mario_() #object created
add_new_flame_counter = 0
global SCORE
SCORE = 0
global HIGH_SCORE
flames_list = &#91;]
pygame.mixer.music.load('mario_theme.wav')
pygame.mixer.music.play(-1, 0.0)
while True:
canvas.fill(BLACK)
check_level(SCORE)
dr.edit() #funtion called
add_new_flame_counter += 1
if add_new_flame_counter == ADD_NEW_FLAME_RATE:
add_new_flame_counter = 0
new_flame = Flames()
flames_list.append(new_flame)
for f in flames_list:
if f.flames_img_rect.left &lt;= 0:
flames_list.remove(f)
SCORE += 1
f.edit()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
mario.up = True
mario.down = False
elif event.key == pygame.K_DOWN:
mario.down = True
mario.up = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
mario.up = False
mario.down = True
elif event.key == pygame.K_DOWN:
mario.down = True
mario.up = False
score_font = font.render('Score:'+str(SCORE), True, GREEN)
score_font_rect = score_font.get_rect()
score_font_rect.center = (200, cactus_img_rect.bottom +
score_font_rect.height/2)
canvas.blit(score_font, score_font_rect)
level_font = font.render('Level:'+str(LEVEL), True, GREEN)
level_font_rect = level_font.get_rect()
level_font_rect.center = (500, cactus_img_rect.bottom +
score_font_rect.height/2)
canvas.blit(level_font, level_font_rect)
top_score_font = font.render('Top
Score:'+str(topscore.high_score),True,GREEN)
top_score_font_rect = top_score_font.get_rect()
top_score_font_rect.center = (800, cactus_img_rect.bottom +
score_font_rect.height/2)
canvas.blit(top_score_font, top_score_font_rect)
canvas.blit(cactus_img, cactus_img_rect)
canvas.blit(fire_img, fire_img_rect)
mario.edit()
for f in flames_list:
if f.flames_img_rect.colliderect(mario.mario_img_rect):
game_over()
if SCORE &gt; mario.mario_score:
mario.mario_score = SCORE
pygame.display.update()
CLOCK.tick(FPS)
Start_the_game()
Output
Implementation
The first code offered is importing all the required libraries. The next
module is the beginning game of Mario. In the next module, the game level,
if you complete the challenge of the particular level. The next module is the
central module of the video game, consisting of Boolean & other loops &
conditions. The next module is the user interface of the game over the
window. Classes are made to keep the data private. Functions are defined and
called. Objects are created to access class data variables and member
functions. Pygame library is used to access the game functions easily. Use of
while true statement, if-else statement, for loop and do while.
Summary
This Mario video game in Python is created in the python interpreter.
Python is extremely smooth to study the syntax underlines readability & it
can decrease time-ingesting in development. Also, this guide is the easiest
way for novices or undergraduates to improve their analytical skills in
coding.
AFTERWORD

Python is a powerful broad programming language that's also interpreted and


may be used for various tasks such as web development, game creation, deep
learning, and data analysis.
Python's popularity is developing fast due to technologies such as
machine learning and artificial intelligence, expanding the prospects for those
who want to work as python developers. As a bonus, English is a fantastic
language for novices since it is simple to learn and comprehend.
This book encompasses a broad variety of topics, including beginning
with the most fundamental questions such as "What is Python?" and
progressing through the use of complex Python tools, such as data structures,
methods, and game programming projects. As a result, it is appropriate for
both beginning and experienced readers.
Additionally, one may test their understanding of a specific subject by
examining the examples that are provided in each chapter and section of the
book. A synopsis of data types, algorithms, and problem-solving techniques
is included in the book for easy review.
Additionally, this book is separated into two sections.: the very first half
will teach you the fundamentals of Python programming, which include
variable initialization, data types, conditional statements, functions, lists,
dictionaries, string manipulation, and so on, including how to create your
own modules in Python; the second half will teach you advanced Python
programming concepts, such as data structures including float, string, list,
tuples, arrays, and algorithms; Search Trees, Bubble Sort, Merge Sort,
Selection Sort, and Quick Sort. The second part will cover object-oriented
programming principles such as polymorphism, encapsulation, inheritance,
multiple inheritances, and operator overloading. A separate Operator
Overloading module is also included, which explains why it is necessary as
well as how to make use of it in the Python programming language.
The second section will introduce object-oriented programming basics
such as polymorphism, encapsulation, inheritance, multiple inheritances, and
operator overloading. Working with databases will also be introduced in the
second section. Additionally, it includes a separate Operator Overloading
module that explains the necessity of and how to utilize it in Python. In
addition, it includes Search Trees, Bubble Sort, Merge Sort, Selection Sort,
and Quick Sort, among other things. Last but not least, it contains Python
Game programming projects.
This book on the fundamentals of Python programming is just the first
step in developing a programmer's abilities. The fact that you have solved all
of the examples supplied indicates that you have gained important expertise
in the fundamental programming concepts using the Python programming
language. You are about to embark on an in-depth study of programming, in
which you will refine your algorithmic thinking skills and subsequently add
technical knowledge about the Python programming language.

You might also like