Python One Shot - 015549
Python One Shot - 015549
Eg: MS Word, MS Excel, Adobe Photoshop, Internet Explorer, Chrome, etc., are examples of computer programs.
<For Editor: Change text below Source Code to - Written in High-level language eg: C, C++, Java, Python etc.>
Python is a versatile, beginner-friendly and a high-level programming language that has gained immense
popularity for its simplicity and readability. It was created by Guido van Rossum and first released in 1991.
Python is widely used in various domains such as web development, data analysis, scientific computing,
artificial intelligence, and automation.
It provides a solid foundation for learning data structures and algorithms (DSA), which are fundamental
concepts in computer science.
You can follow the given steps to install Python Interpreter to start coding
Visit the official Python website at http://www.python.org
Navigate to the Downloads section and choose the appropriate Python version for your operating system
(Windows, macOS, or Linux)
Click on the download link for the desired version, such as Python 3.9.5(Note: Python 2 versions and Python 3
versions are not compatible. In this series, we are going to learn Python 3, so you can download any version
of Python 3, like Python 3.9.5.).
Cracking the Coding Interview in Java - Foundation
Once the download is complete, locate the installer file and double-click to run it
In the installer, select the option to "Install Now" or customize the installation if desired
On Windows, make sure to check the box that says "Add Python to PATH" to make it accessible from the
command line
Follow the prompts and agree to the license terms to proceed with the installation
The installer will install Python and its associated tools onto your system
Once the installation is complete, you can verify it by opening a terminal or command prompt and typing
"python --version" or "python3 --version" to see the installed Python version
Congratulations! You now have Python installed on your system and can start writing and executing Python
programs.
Once you are done with the installation, you can write Python code in a text editor or an Integrated
Development Environment (IDE) like PyCharm or Visual Studio Code, and then execute it using the Python
interpreter.
Python uses a straightforward syntax that emphasizes code readability. Here's an example of a simple Python
program that prints "Hello, World!":
print("Hello, World!")
Congratulations!! You have successfully written your first Python program. In Python, the print() function is used
to display output on the screen. It allows you to print text, variables, and expressions.
To move to the next line while printing in Python, you can use the special character \n, which represents a
newline. Here's an example:
print("Hello,\nWorld!")
In the above code, the \n character is inserted between "Hello," and "World!" to create a line break.
You can also achieve the same effect by using the end parameter of the print() function. By default, the end
parameter is set to "\n", which adds a newline character at the end of each print() statement. You can change
the value of end to achieve different behaviors:
print("World!")
In this example, the end parameter is set to a space character " ", which appends a space instead of a newline
character. Therefore, the second print() statement starts on the same line as the first one.
Python CL
A command-line interface or command language interpreter (CLI) is a means of interacting with a
computer program where the user (or client) issues commands to the program in the form of successive
lines of text
Having even just a very basic command-line interface (CLI) for your program can make everyone’s life
easier for modifying parameters, including programmers, but also non-programmers.
Comments in Python
Comments in Python are used to add explanatory or descriptive text to your code. They are ignored by the
Python interpreter and do not affect the execution of the program. Comments help improve code readability
and make it easier for others (or yourself) to understand the purpose or functionality of specific code sections.
1. Single-Line Comments:
Single-line comments begin with the # symbol and continue until the end of the line. They are used to
comment on a single line or provide brief explanations.
x = 5 # Assigning a value to x
2. Multi-Line Comments:
Multi-line comments, also known as docstrings, are used for longer comments that span multiple lines. They
are enclosed between triple quotes (' ' ' or " " ") and can be used to describe functions, classes, or modules.
'''
'''
Comments can also be used to temporarily disable or "comment out" sections of code that you don't want to
execute. By commenting out code, you can easily enable or disable specific sections without having to delete
or rewrite the code.
Remember, comments are a valuable tool for communicating and documenting your code, so use them wisely
to make your code more understandable and maintainable.
Python Indentation
In Python, indentation plays a crucial role in determining the structure and execution of code blocks. Unlike
many other programming languages that use braces or brackets, Python uses indentation to define blocks of
code.
1. Consistent Indentation: Python requires consistent indentation using spaces or tabs throughout the code. It
is recommended to use four spaces for indentation.
2. Code Blocks: Indentation is used to define code blocks, such as loops, conditionals, and functions. The
statements within a block are indented at the same level.
4. No Explicit Braces: Python does not use braces ({}) or explicit keywords (e.g., "begin" and "end") to define
code blocks. Instead, the indentation itself determines the scope and nesting of code.
5. Syntax Error Prevention: Indentation errors, such as inconsistent or missing indentation, will result in syntax
errors and can cause the code to fail during execution.
Example:
if condition:
statement1
statement2
if nested_condition:
nested_statement1
nested_statement2
statement3
statement4
Braces are not used in Python, instead Indentation is used. However, for clear understanding let us look at how
will braces representation of above pseudo code will look like:
if condition{
statement1
statement2
if nested_condition{
nested_statement1
nested_statement2
statement3
statement4
Note: Proper indentation in Python is not just a matter of style; it is a fundamental part of the language's syntax.
So, it's important to be mindful of indentation to ensure your code is both readable and functional.
Variables
Variables in Python are used to store and manipulate data. They act as containers that hold values of different
types, such as numbers, strings, lists, or more complex data structures. Here's how you can work with variables
in Python:
Note: Variables do not need to be declared with any particular type in Python
Variable Assignment:
You can assign a value to a variable using the assignment operator =. The variable is created if it doesn't exist
or updated with the new value if it already exists.
x = 10
name = "Alice"
pi = 3.1
a. Variable names can contain letters (a-z, A-Z), digits (0-9), and underscores (_).
c. Variable names are case-sensitive, so myVar and myvar are different variables.
d. It's good practice to use descriptive variable names that reflect their purpose.
e. Generally, we follow the convention of naming variables using snake_case, rather than lowerCamelCase
Variable Types:
Python is dynamically typed, meaning you don't need to specify the variable type explicitly. The type is inferred
based on the assigned value. For example: In C++ or Java, we explicitly tell for each variable its data type, i.e.
int x = 5; But in python we need not do that.
x = 10 # Integer
pi = 3.14 # Float
Variable Reassignment:
x = 5
print(x) # Output: 5
x = "Hello"
Variable Concatenation:
first_name = "John"
last_name = "Doe"
Multiple Assignments:
x, y, z = 1, 2,
Variable Scope:
The scope of a variable determines where it can be accessed within the code. Variables defined inside a
function have local scope and can only be accessed within that function. Variables defined outside any
function have global scope and can be accessed throughout the program.
# Global variable
def my_function():
# Local variable
my_function()
Keywords in Python
Keywords in Python are reserved words that have special meanings and predefined functionalities within the
Python language. These keywords cannot be used as variable names or identifiers because they are already
These keywords serve specific purposes in Python and play a vital role in constructing valid and meaningful
code. For example, keywords like if, else, for, and while are used in control flow statements, while def is used to
define functions. Other keywords, such as import, from, and as, are used for importing modules and managing
namespaces.
Note: It's important to note that keywords are case-sensitive, so True, False, and None must be written in title
case.
As a best practice, avoid using these keywords(with different casing) as variable names or identifiers to
Question: Which of the following variable names are valid in Python and which ones are invalid?
1. my_var
2. 123abc
3. _count
4. first name
5. total#sales
6. True
7. user@name
8. var2
9. $price
10. function()
Answer:
Invalid: 123abc (starts with a number), first name (contains a space), total#sales (contains a special
character), True (reserved keyword), user@name (contains special character "@"), $price (starts with a special
In Python, the print() function is used to display output on the screen. It allows you to print text, variables, and
expressions. Here are some examples of how to use the print() function:
1. Printing Text
print("Hello, World!")
2. Printing Variables
name = "Alice"
age = 25
3. Printing Expressions
x = 10
y = 5
This functionality is available only for versions of Python 3.6 and after.
name = "Bob"
age = 30
x = 1
y = 2
z = 3
6. Printing to a File
Variables in Python can be updated by assigning new values to them using the assignment operator (=).
Example:
my_variable = 42
my_variable = 23
my_variable = "School"
Data Types
In Python, data types represent the kind of values that can be stored and manipulated in variables. Python
provides several built-in data types to handle different types of data. Here are some commonly used data
types in Python:
a. Numeric Types
int: Represents integers (whole numbers) like 3, -7, 100, etc. (In Python 3, the int type has no max limit. You
can handle values as large as the available memory allows. Python 2 has two integer types, int and long ,
while Python 3 only has int
float: Represents floating-point numbers (real numbers) with decimal points like 3.14, -0.5, 2.0, etc
complex: Represents complex numbers with real and imaginary parts, like 2 + 3j, -1 + 0.5j, etc.
b. Text Type
str: Represents strings of characters enclosed in single quotes (' ') or double quotes (" "). Strings are used to
store textual data, such as names, sentences, or any sequence of characters. For example: ‘sentence’,
“mytext”, “abcd”, etc.
c. Boolean Type
bool: Represents boolean values, which can be either True or False. Booleans are often used in conditional
statements and logical operations.
d. Sequence Types
list: Represents an ordered collection of items enclosed in square brackets ([]). Lists can contain elements of
different types and can be modified (mutable). For example: thislist = ["apple", "banana", "cherry"], mylist =
[1,2,3,4]
tuple: Similar to lists, but enclosed in parentheses (()). Tuples are immutable, meaning they cannot be
modified after creation and allow duplicate values. For example: thistuple = ("apple", "banana", "cherry",
"apple", "cherry"), mytuple = (1, 2, 2, 3).
e. Mapping Type
dict: Represents a collection of key-value pairs enclosed in curly braces ({}) or created using the dict()
constructor. Dictionaries allow you to store and retrieve values based on unique keys.
student = {
"first_name": "Ally",
"last_name": "Joy",
"rollno": 96378
f. Set Types
set: Represents an unordered collection of unique elements enclosed in curly braces ({}) or created using
the set() constructor. Sets are useful for performing mathematical set operations, such as union,
intersection, etc. For example: thisset = {"apple", "banana", "cherry"}
e. Other Types
None: Represents a special value indicating the absence of a value or a null value.
These are the fundamental data types in Python. However, Python is a dynamic language, allowing you to
create and work with more complex data structures and custom types using classes and objects. Additionally,
Python also supports type conversion and type-checking mechanisms to handle different data types
effectively during the runtime.
ASCII Values
ASCII (American Standard Code for Information Interchange) is a widely used character encoding standard
that provides a way to represent characters as numeric codes. It was developed in the 1960s and became a
standard in the computing industry.
ASCII uses 7 bits to represent a total of 128 characters, including uppercase and lowercase letters, digits,
punctuation marks, control characters, and some special symbols. Each character is assigned a unique
numeric code from 0 to 127.
- Special Characters: Various special characters like punctuation marks, symbols, and control characters have
their own ASCII values, such as:
ASCII was widely used in older systems and programming languages. However, its limitations in representing
diverse languages and scripts led to the development of Unicode, a more comprehensive encoding standard
capable of representing a broader range of characters from various writing systems.
In Python, you can obtain the Unicode code of a special character by using the built-in ord() function and the
ASCII value of characters. The ord() function accepts a string of unit length as an argument and returns the
Example:
string = "€"
unicode_code = ord(string)
Output:
Unicode of €=8364
Conversely, Python chr() function is used to get a string representing of a character which points to a Unicode
code integer. For example, chr(97) returns the string 'a'. This function takes an integer argument and throws an
error if it exceeds from the specified range. The standard range of the argument is from 0 to 1,114,111.
Taking Input
In Python, you can take user input using the input() function. It allows you to prompt the user for input and
retrieve the entered value as a string. Here's how you can use the input() function:
In the above example, the input() function is used to prompt the user with the message "Enter your name: ". The
program waits for the user to enter input, and once the user presses Enter, the entered value is assigned to the
variable name.
If you want to convert the user input to a specific data type, such as integer or float, you can use type casting.
Type Casting is the method to convert a Python variable datatype into a certain data type in order to perform
To know the type of data stored in a variable, we can use the type() function.
For example, to capture an integer input, you can use the int() function:
In this case, the input is first captured as a string using input(), and then it is converted to an integer using int().
Taking user input using input() allows your Python programs to interact with users and make them more
Code:
Write a program to find the sum of two numbers entered by the user.
Solution: https://pastebin.com/t9cwVV5r
Operators in Python
These are symbols or special characters that perform various operations on operands (variables, values, or
expressions). Python supports a wide range of operators, including arithmetic operators, assignment operators,
comparison operators, logical operators, and more. The different types of operators in Python are:
1. Arithmetic Operators:
4. / (Division): Divides the left operand by the right operand (returns a float).
5. // (Floor Division): Divides the left operand by the right operand and rounds down to the nearest whole
number.
6. % (Modulus): Returns the remainder of the division between the left operand and the right operand.
7. ** (Exponentiation): Raises the left operand to the power of the right operand.
2. Assignment Operators:
a. = (Assignment): Assigns the value on the right to the variable on the left.
b. +=, -=, *=, /=, //=, %=: Perform the corresponding operation and assign the result to the left operand.
3. Comparison Operators:
c. < (Less than), > (Greater than): Checks if the left operand is less than or greater than the right operand,
respectively.
d. <= (Less than or equal to), >= (Greater than or equal to): Checks if the left operand is less than or equal to, or
greater than or equal to the right operand, respectively.
Logical Operators:
a. & (Bitwise AND): Performs a bitwise AND operation between the binary representation of the operands.
b. | (Bitwise OR): Performs a bitwise OR operation between the binary representation of the operands.
c. ^ (Bitwise XOR): Performs a bitwise XOR (exclusive OR) operation between the binary representation of the
operands.
d. << (Left Shift): Shifts the bits of the left operand to the left by the number of positions specified by the right
operand.
e. >> (Right Shift): Shifts the bits of the left operand to the right by the number of positions specified by the right
operand.
6. Membership Operators:
7. Identity Operators: Identity operators are used to compare the objects, not if they are equal, but if they are
actually the same object, with the same memory location.
b. is not: Returns True if two operands do not refer to the same object.
Take the radius from the user and display the volume of sphere.
Code: https://pastebin.com/rA798mzq
Take the principal, rate and time from the user and display the simple interest.
Code: https://pastebin.com/3wRK7A7Q
Operator Precedence
Operator precedence in Python determines the order in which operators are evaluated in an expression. Here is
the precedence of operators in Python, listed in descending order from highest to lowest:
1. Parentheses: ()
2. Exponentiation: **
7. Bitwise XOR: ^
9. Comparison Operators: ==, !=, >, <, >=, <=, is, is not, in, not in
It is important to note that parentheses can be used to override the default precedence and explicitly define
Example:
result = 3 + 2 ** 4 / 2 * 5 - 8 // 2
print(result)
Output:
39.0
Typecasting
Typecasting, also known as type conversion, is the process of changing the data type of a variable from one
type to another. In Python, you can perform typecasting using built-in functions or constructor functions for the
type(
Python type() is a built-in function that is used to return the type of data stored in the objects or variables in
the program
For example, if a variable contains a value of 45.5 then the type of that variable is float
If variable ‘subj’ stores the value ‘Python’, then the type of ‘subj’ is a string.
A. Integer to String
num = 42
num_str = str(num)
print(num_str, type(num_str))
B. String to Integer
num_str = "123"
num = int(num_str)
print(num, type(num))
C. Float to Integer
float_num = 3.14
integer_num = int(float_num)
print(integer_num, type(integer_num))
Note: it is important to ensure compatibility and proper handling of data in different contexts.
Solution: https://pastebin.com/YcPYnvtf
MCQs
x = 10
y = 5
(a) True
(b) False
(c) Error
(d) None
x = 5
y = "2"
print(x + y)
(a) 7
(b) 52
(c) TypeError
(d) "52"
8 // 3 + 4 % 2
(b) 3.67
(c) 3.5
(d)
(a) 0
(b) 0.2857
Answers
(a) Tru
(c) TypeErro
(d)
(b) 0.2857
Chapter-2
Control Statements
Control statements are structures in programming languages that enable you to control the flow of execution
in your program. They allow you to make decisions, repeat certain actions, and handle exceptional cases.
1. Conditional Statements: Enable execution of specific code blocks based on the evaluation of conditions.
These are:
i
eli
else
2. Looping Statements: Allow repetition of code blocks either for a specified number of times or until a condition
is met. There are two loops in python
fo
while
3. Control Flow Statements: Provide control over the flow of execution, such as terminating loops or skipping
iterations based on certain conditions. These are:
brea
continu
pass
Conditionals
Conditionals, also known as conditional statements or conditional expressions, are structures in programming
languages that allow the execution of different code blocks based on specified conditions. They provide the
ability to make decisions and control the flow of a program.
if elif else
if: The if statement allows you to execute a block of code if a condition is true
elif: The elif statement is short for "else if" and is used to check additional conditions if the condition for if
statement is False. It is optional and can appear multiple times within an if statement
else: The else statement is used to define a block of code that should be executed if none of the preceding
conditions are true.
Conditionals evaluate a condition or expression that results in a Boolean value (True or False). Based on the
evaluation of the condition, the program decides which code block(s) to execute.
if else condition
Example: Take an integer input and tell if it is positive or negative (zero not included)
Code: https://pastebin.com/D2PyHGb4
Code: https://pastebin.com/XU1YUhdT
Example: If cost price and selling price of an item is input through the keyboard, write a program to determine
whether the seller has made profit or incurred loss or no profit no loss. Also determine how much profit he
made or loss he incurred.
Code: https://pastebin.com/CYvA29Ka
In Python, the logical operators "and" and "or" are used for combining multiple conditions.
if condition1 or condition2:
x = 5
y = 10
In this example, the first "if" statement will execute the code block since both conditions x > 0 and y < 15 are true.
The second "if" statement will also execute the code block because at least one of the conditions x > 10 or y < 15
is true.
Question: Take positive integer input and tell if it is a four digit number or not.
Code: https://pastebin.com/ey65ydhP
Code: https://pastebin.com/2vPKxZXW
Code: https://pastebin.com/eNAtPUh4
Question: Take 3 positive integers input and print the greatest of them.
Code: https://pastebin.com/KxCiyWZd
Code: https://pastebin.com/ztKvKnSP
The nested if-else syntax allows you to have an if-else statement inside another if or else block. This allows for
more complex branching and decision-making in your code.
Syntax:
if condition1:
if condition2:
# Code block executed when both condition1 and condition2 are true
else:
else:
Question: Take positive integer input and tell if it is divisible by 5 or 3 but not divisible by 15.
Code: https://pastebin.com/YXpsTA2B
Question: Take 3 positive integers input and print the greatest of them (without using multiple condition)
Code: https://pastebin.com/H5S4rXV2
The elif statement in Python is short for "else if" and is used to introduce an additional condition to be checked if
the preceding if or elif conditions are false. It allows for multiple conditional branches in your code.
Syntax:
if condition1:
elif condition2:
else:
# Code block executed when both condition1 and condition2 are false
Question: Take input percentage of a student and print the Grade according to marks:
2) 61-80 Good
3) 41-60 Average
4) <=40 Fail
Code:
Match Case
Python does not have a built-in switch statement like some other programming languages (e.g., C/C++, Java).
Instead, Python relies on other control flow structures to handle multiple conditions and make decisions based
on different cases.
However, Python 3.10 introduced the match statement as a form of pattern matching. The match statement is
similar to the traditional switch statement. So we can implement switch statement using the match and case
Syntax: https://pastebin.com/xNKpXVDm
Example: https://pastebin.com/LwZv0ZxP
Ternary Operator
The ternary operator in Python is a concise way to express conditional expressions. It allows you to assign a
Syntax:
In this syntax, condition is the condition to be evaluated. If the condition is true, the value assigned to variable is
Question: Write a program to check if number is odd or even using ternary operator.
Code: https://pastebin.com/uPdJm7fk
Loops in programming allow you to repeatedly execute a block of code based on a specific condition. They are
used to automate repetitive tasks and iterate over collections of data.
In Python, there are two types of loops: for loop and while loop.
for loo
A for loop is used to iterate over a sequence (such as a list, tuple, or range) or any iterable object
It executes a block of code for each item in the sequence
The loop variable takes on the value of each item in the sequence one by one
The loop continues until all items in the sequence have been processed.
Syntax:
Example:
for i in [1,2,3,4]:
print(i)
Question: Print hello world ‘n’ times. Take ‘n’ as input from user.
Code: https://pastebin.com/Bp50vKm3
1. Initialization: The loop starts by initializing a loop variable. This variable can be any valid variable name and
is used to represent each item in the sequence. In each iteration, the loop variable takes on the value of the
next item in the sequence.
2. Iteration: The loop iterates over the sequence and executes the code block for each item in the sequence.
The loop variable takes on the value of each item, one by one, as the loop progresses through the sequence.
3. Execution: The code block within the loop is executed for each iteration. You can include any statements or
operations within the code block that you want to perform for each item in the sequence.
4. Termination: The loop continues iterating until it reaches the end of the sequence. Once all items in the
sequence have been processed, the loop terminates, and the program proceeds to the next line of code after
the loop.
false
exit
for
Statements loop
Code: https://pastebin.com/yUZahTNj
In this program, the range(start, stop) function is used to generate a sequence of numbers from 1 (inclusive) to
101 (exclusive). The loop variable num takes on the value of each number in the sequence, starting from 1 and
ending at 100.
Code: https://pastebin.com/z4w1q8wr
In this program, the range(start, stop, step) function is used to generate a sequence of numbers starting from 2
(inclusive) and ending at 101 (exclusive), with a step size of 2. This ensures that only even numbers are included
in the sequence.
Code: https://pastebin.com/FP6zpL5d
First term = a
Second term = a + d
Break Statement
The break statement is a control flow statement in Python that allows you to exit or terminate a loop
prematurely. It is commonly used to stop the execution of a loop before it reaches its natural end. When
encountered, the break statement immediately terminates the innermost loop and resumes execution at the
next statement following the loop.
Example:
if num == 6:
break
print(num)
Once the loop encounters the break statement when num is 6, the loop is immediately terminated, and the
numbers 6, 7, 8, 9, and 10 are not printed.
The break statement is particularly useful when you need to exit a loop prematurely based on certain
conditions or when you have found the desired result. It helps control the flow of your program and allows you
to efficiently manage looping behavior.
Continue Statement
The continue statement is a control flow statement in Python that is used to skip the rest of the current iteration
of a loop and move on to the next iteration. When encountered, the continue statement immediately stops the
execution of the current iteration and goes back to the loop's beginning to start the next iteration.
if num % 2 == 0:
continue
print(num)
In this example, only the odd numbers are printed. Because, whenever the loop encounters an even number,
the continue statement is executed, skipping the print(num) statement and proceeding to the next iteration.
The continue statement is useful when you want to skip certain iterations of a loop based on specific
conditions. It allows you to control the flow of your program within the loop and selectively execute the desired
statements.
Pass Statement
The pass statement in Python is a placeholder statement that does nothing. It is used when a statement is
required syntactically but you don't want to execute any code or perform any action. The pass statement is
typically used as a placeholder to indicate that the code will be added later.
Example:
if num == 3:
pass
else:
print(num)
As you can see, when num is 3, the loop iteration doesn't print anything due to the pass statement. For all other
values of num, the print(num) statement is executed as usual.
The pass statement is useful in situations where you want to create a placeholder for future code, handle
specific conditions separately in the future, or simply maintain the syntactic correctness of the code without
any immediate action.
Code: https://pastebin.com/9JFyXAaP
while loo
A while loop is used to repeatedly execute a block of code as long as a specific condition is true
It evaluates the condition before each iteration and continues until the condition becomes false
It is useful when the number of iterations is not known beforehand or depends on runtime conditions.
Syntax:
while condition:
Code: https://pastebin.com/cqXN9BXq
Code: https://pastebin.com/x5szMgbj
Question-1:
j = 0
print(j)
j = j + 1
Answer:
10
Question-2:
x = 1
while x == 1:
x = x - 1
print(x)
Answer:
Question-3
i = 10
while i == 20:
print("computer buff!")
Answer:
Question-4
x = 4
y = 0
while x >= 0:
x -= 1
y += 1
continue
else:
print(x, y)
Answer:
3 1
1 3
0 4
-1 5
For n = 1
*****
For n = 2
*****
*****
For n = 3
*****
*****
*****
Code: https://pastebin.com/VDSzDSrZ
1234 123456
1234 123456
1234 123456
1234 123456
123456
123456
Code: https://pastebin.com/MUyf5ygU
For n = 4
**
***
****
Code: https://pastebin.com/iinDpDrP
For n = 4
1 2
1 2 3
1 2 3 4
Code: https://pastebin.com/qbz6VTCm
1
1 2 3
1 2 3 4 5
1 2 3 4 5 6 7
Code: https://pastebin.com/WKm4aDKG
Chapter-3
Python Arrays
3. However, to work with arrays in Python you will have to import a library, like the NumPy library.
Python Collections(Arrays)
1. Lists
2. Tuple
3. Set
4. Dictionary
Lists
Syntax:
List Items:
1. Indexed: List items are indexed. The first item has index [0], the second item has index [1] etc.
2. Ordered: the items have a defined order, and that order will not change. If you add new items to a list, the
3. Mutable: The list is mutable, meaning that we can update, add, and remove items in a list after it has been
created.
list2 = [1, 5, 7, 9, 3]
Type of a lis
Since a list is a built in datatype in python, its type is List
print(type(mylist))
Length of a lis
To determine how many items a list has, we use the len() function
print(len(my_list))
Code:
this_list = []
element = int(input())
this_list.append(element)
print(this_list)
Accessing Items
List items are indexed and you can access them by referring to the index number in 4 ways:
3. Range of Indexes: You can specify a range of indexes by specifying where to start and where to end the
range. When specifying a range, the return value will be a new list with the specified items. Herein, the start of
the range is inclusive and the end of the range is exclusive, i.e., elements are considered from start index till end
index-1.
4. Range of Negative Indexes: Specify negative indexes if you want to start the search from the end of the list.
Herein, the start of the range is inclusive and the end of the range is exclusive, i.e., elements are considered
from start index till end index-1.
1. append(): To add an item to the end of the list, we use the append() method.
2. insert(): To insert a list item at a specified index, we use the insert() method.
method.
2. pop(): The pop() method removes the specified index. If you do not specify the index, the pop() method
1. At an index:
2. In a range:
List objects have a sort() method that will sort the list alphanumerically, ascending, by default.
Reversing a list
List Comprehension
List comprehension offers a shorter syntax when you want to create a new list based on the values of an
existing list.
For Example: Based on a list of fruits, you want a new list, containing only the fruits with the letter "a" in the
name. Without list comprehension you will have to write a for statement with a conditional test inside but with
list comprehension you can do all that with only one line of code.
Copying a list
You cannot copy a list simply by typing list2 = list1, because: list2 will only be a reference to list1, and changes
made in list1 will automatically also be made in list2.
Similarly, extend() method can also be used to add the elements of any iterable to the end of the list.
Nested Lists
Q1. Given a list in Python and provided the index of the elements, write a program to swap the two elements
in the list.
Examples:
Input : List = [23, 65, 19, 90], index1 = 0, index2 = 2Output : [19, 65, 23, 90]
Code:
temp=lis[index1]
lis[index1]=lis[index2]
lis[index2]=temp
return lis
# Driver function
index1, index2 = 0, 2
Q2. Given a list of integers with duplicate elements in it. The task is to generate another list, which contains
only the duplicate elements. In simple words, the new list should contain elements that appear more than
one.
Examples:
Input : list = [10, 20, 30, 20, 20, 30, 40, 50, -20, 60, 60, -20, -20]
Code:
list = [1, 2, 1, 2, 3, 4, 5, 1, 1, 2, 5, 6, 7, 8, 9, 9]
new = []
for a in list:
n = list.count(a)
if n > 1:
new.append(a)
print(new)
TUPLES
Syntax:
Tuple item
Ordered: When we say that tuples are ordered, it means that the items have a defined order, and that order
will not change
Immutable: Tuples are unchangeable, meaning that we cannot update, add or remove items after the tuple
has been created
Duplicates allowed: Tuple can have items with the same value. For example: this_tuple = ("apple",
"banana", "cherry", "apple", "cherry") is a valid tuple
Any datatype: A tuple can have can type of data. For example: all the 3 following tuples are valid.
tuple2 = (1, 5, 7, 9, 3)
To create a tuple with only one item, you have to add a comma after the item, otherwise Python will not
recognize it as a tuple.
To determine how many items a tuple has, use the len() function.
Type of a tuple
A tuple is a built in datatype in python, hence it is of the ‘tuple’ datatype. This can be confirmed by using the
type() method.
tuple() constructor
1. Positive Index: You can access tuple items by referring to the index number(0 to length of tuple-1), inside
square brackets.
2. Negative Indexing: Negative indexing means start from the end. -1 refers to the last item, -2 refers to the
second last item etc.
4. Range of negative indexes: Specify negative indexes if you want to start the search from the end of the
tuple. Herein, the start of the range is inclusive and the end of the range is exclusive, i.e., elements are
considered from start index till end index-1.
Unpacking a Tuple
When we create a tuple, we normally assign values to it. This is called "packing" a tuple. But, in Python, we are
also allowed to extract the values back into variables. This is called "unpacking".
Note: We will get an error if the number of variables used for unpacking does not match the number of items in
the tuples.
Concatenation of tuples
Note: Tuple does not support functions like append(), insert() and extend() as tuples are immutable.
Tuples vs List
Iterating through a ‘tuple’ is faster than in a ‘list’
‘Lists’ are mutable whereas ‘tuples’ are immutable
Tuples that contain immutable elements can be used as a key for a dictionary.
Approach: Using the reversed() built-in function, reverse the elements of the tuple and put it in a list. Once the
list has all the elements in reverse order, typecast it into a tuple.
Code:
Syntax:
Set Item
Unordered: Unordered means that the items in a set do not have a defined order. Set items can appear in a
different order every time you use them, and cannot be referred to by index or key
Immutable: Set items are unchangeable, meaning that we cannot update the items after the set has been
created. Addition and removal is possible
Unindexed: Sets are unordered, so you cannot be sure in which order the items will appear
Duplicates not allowed: Sets cannot have two items with the same value. If you put duplicates in the set,
they get ignored. Note: 1 and true are considered same values in sets and hence are considered as
duplicates id entered in the same set
Any datatype: A set can have items of any data type. All the 3 following sets are valid.
set2 = {1, 5, 7, 9, 3}
Length of a set
To determine how many items a set has, use the len() function.
In the below example, see how both the sets have the same length although the first set has 1 extra duplicate
element.
Set is a built-in datatype in python, hence its of set datatype. This can be verified by using the type() method.
Accessing items
But you can loop through the set items using a for loop.
To add any iterable, i.e., set, tuple, etc. to a set, use the update() method.
remove() throws error if item is not present in the set whereas discard() does not.
Joining 2 sets
You can use the union() method that returns a new set containing all items from both sets.
The update() method inserts all the items from one set into another.
The update() method inserts all the items from one set into another.
Keep all except duplicates: The symmetric_difference_update() method will keep only the elements that
are NOT present in both sets.
Output : max is 65
Output : min is 4
Approach: The built-in function max() and min() in Python are used to get the maximum and minimum of all
the elements in a set.
Code:
def MIN(sets):
return (min(sets))
def MAX(sets):
return (max(sets))
# Driver Code
print(MIN(sets))
print(MAX(sets))
Q2. Given three arrays, we have to find common elements in three sorted lists using sets.
Examples :
Output : [5]
Approach: We have given three arrays, with the help of sets one can easily find out the intersection of these
Arrays. Intersection method simply provides the intersection of both the arrays upon which you want to
perform the operation of intersection (or, it simply gives out the common elements in both the array). We will
be taking three arrays and then we will take out the intersection.
Code:
Dictionary
Dictionaries are written with curly brackets, and have keys and values
Syntax:
this_dict = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
Dictionary Item
Ordered: When we say that dictionaries are ordered, it means that the items have a defined order, and that
order will not change
Changeable: Dictionaries are changeable, meaning that we can change, add or remove items after the
dictionary has been created
Unindexed:
Duplicates not allowed: Dictionaries cannot have two items with the same key
Any datatype: The values in dictionary items can be of any data type For example:
thisdict = {
"brand": "Ford",
"electric": False,
"year": 1964,
To determine how many items a dictionary has, use the len() function.
Type of a dictionary
You can access the items of a dictionary by referring to its key name, inside square brackets. Also, There is also
a method called get() that will give you the same result.
You can change the value of a specific item by referring to its key name.
Any other dictionary or any iterable having key value pairs can also be added to a dictionary using update()
method.
The pop() method removes the item with the specified key name.
del: The del keyword removes the item with the specified key name.
Copying a dictionary
You cannot copy a dictionary simply by typing dict2 = dict1, because: dict2 will only be a reference to dict1, and
changes made in dict1 will automatically also be made in dict2.
There are ways to make a copy, one way is to use the built-in Dictionary method copy().
To access items from a nested dictionary, you use the name of the dictionaries, starting with the outer
dictionary.
Q1. Given a dictionary in Python, write a Python program to find the sum of all items in the dictionary.
Examples:
Approach: Use the sum function to find the sum of dictionary values.
Code:
this_dict = {
'a': 100,
'b': 200,
'c': 300
print(sum(this_dict.values()))
of the string in alphabetical order. In mirror operation, we change ‘a’ to ‘z’, ‘b’ to ‘y’, and so on.
Examples:
Input : N = 3
paradox
Output : paizwlc
Input : N = 6
pneumonia
Output : pneumlmrz
Code:
def mirrorChars(input,k):
# create dictionary
original = 'abcdefghijklmnopqrstuvwxyz'
reverse = 'zyxwvutsrqponmlkjihgfedcba'
dictChars = dict(zip(original,reverse))
# characters in mirror
prefix = input[0:k-1]
suffix = input[k-1:]
mirror = ''
for i in range(0,len(suffix)):
print (prefix+mirror)
# Driver program
if __name__ == "__main__":
input = 'paradox'
k = 3
mirrorChars(input,k)
colors[2]
MCQ-2
"yellow" in colors
a. False
b. Correct
c. 4
MCQ-3
Square brackets in an assignment statement will create which type of data structure?
( s=[] )
a. List
b. Tuple
c. Set
d. Dictionary
MCQ-4
a. Array of tuples
b. Tuples of lists
c. List of tuples
d. Invalid type
MCQ-5
b. (4, 6)
c. (1, 2, 3, 4)
d. Invalid Syntax
Functio
Functions in Python are blocks of reusable code that perform a specific task.
They help organize code, promote reusability, and make programs easier to understand and maintain.
Functions are defined using the def keyword, followed by the function name, required and optional
parameters in parentheses, return type, and a colon.
The function block is indented and contains the code to be executed when the function is called.
Why Function
Modularity: Functions break down complex programs into smaller, manageable parts, improving code
organization and structure
Reusability: Functions allow code to be reused, saving time and effort by avoiding code duplication
Abstraction: Functions provide a high-level overview, hiding implementation details and making code more
readable and understandable
Decomposition: Functions help break down complex problems into smaller, more manageable tasks,
simplifying the problem-solving process
Code Maintenance: Functions make code easier to maintain and debug by isolating and focusing on
specific parts of the program.
Types of Function
1. Build-in function: These are pre-defined functions that come with Python, such as print(), len(), input(),
range(), and type(). They are readily available for use without requiring any additional code.
2. User-defined function: These functions are created by the user to perform specific tasks. They provide a way
to modularize code and promote reusability. User-defined functions are defined using the def keyword.
Creating a function
Syntax:
def function_name(parameters):
# ...
return value
#statement Body of
Statement
return
Function return
Calling a function
Once a function is defined, it can be called by using its name followed by parentheses. Arguments can be
passed into the function if it expects parameters.
Syntax:
def say_hello():
print("Hello!")
say_hello()
Output:
Hello!
Arguments
In Python, arguments are values passed to a function when calling it. They provide input to the function and
allow it to perform its task using the provided data.
Syntax:
Types of Argument
Default argument
These are parameters in a function that have a default value assigned. If an argument is not provided for a
default parameter, the default value is used.
Example:
print(message, name)
greet("Bob") # Uses the default value "Hello" for the message parameter
These arguments are passed to a function using the name of the parameter they correspond to, followed by
the = sign. The order of keyword arguments does not matter.
greet(age=25, name="Alice")
3. Positional arguments
These are arguments passed to a function in the order specified by the function's parameter list. The values are
assigned to the corresponding parameters based on their position.
Example:
sum = x + y
print("Sum:", sum)
Example:
def sum_numbers(*args):
total = 0
total += num
return total
result = sum_numbers(1, 2, 3, 4)
print(result) # Output: 10
Example:
def display_info(**kwargs):
# Output:
# name: Alice
# age: 25
Parameters
Parameters are the variables listed in the function definition
They act as placeholders for the values that will be passed to the function when it is called
Parameters are specified within the parentheses in the function definition
Parameters define what values a function expects to receive when it is called, and they have local scope
within the function.
Arguments
Arguments are the actual values passed to a function when it is called
They correspond to the parameters defined in the function
Arguments are specified within the parentheses in the function call
Arguments provide the necessary data to the function for it to perform its task.
Parameters
Functional Definition
def add (a,b):
return a+b
Function call
add (2,3):
Arguments
Return Type
The return type of a function refers to the type of value that the function returns when it is executed. The return
type can be explicitly specified using type annotations or implicitly determined based on the value returned by
the function.
Example:
return a + b
In this example, the function add_numbers takes two parameters a and b, both of type int, and it explicitly
specifies the return type as int using the -> syntax. The function performs the addition operation and returns the
result, which is of type int.
In the absence of type annotations, Python infers the return type based on the value returned by the function.
Example:
def is_even(n):
if n % 2 == 0:
return True
else:
return False
In this case, the function is_even does not have an explicitly specified return type. However, based on the True
and False values returned, Python infers that the return type is bool.
Note: It's important to note that in Python, a function can also return None if there is no explicit return statement,
or if the return statement does not provide a value.
<SKIP>
return and yield are both used in Python for returning values from functions or generators, but they have
different behaviors and use cases.
return is used in regular functions to terminate the function's execution and immediately return a value to the
caller. When a return statement is encountered in a function, the function stops executing, and the value
provided after return is passed back to the caller. After a return statement is executed, the function is finished,
and any subsequent code is not executed.
Example:
return a + b
result = calculate_sum(3, 5)
print(result) # Output: 8
In this example, the calculate_sum function uses return to return the sum of a and b back to the caller.
yield is used in generators to create iterable objects that can be iterated over using a loop or other iterator-
consuming constructs. When a generator function encounters a yield statement, it suspends its execution and
yields a value to the caller. The function's state is saved, allowing it to be resumed later. Each time the generator
is iterated, it executes until it reaches the next yield statement.
Example:
def generate_numbers():
for i in range(5):
yield i
numbers = generate_numbers()
print(num) # Output: 0, 1, 2, 3, 4
In this example, the generate_numbers function is a generator that yields numbers from 0 to 4. The yield
statement allows the function to generate one value at a time, and the function's state is saved between
iterations.
In summary, return is used in regular functions to immediately return a value and terminate the function, while
yield is used in generators to create iterable objects and allow incremental value generation.
Nested functions
Nested functions in Python refer to defining a function inside another function. The inner function has access to
the scope of the outer function, including its variables, parameters, and other nested functions.
Example:
def outer_function():
def inner_function():
result = x + y
return result
return inner_function()
output = outer_function()
print(output) # Output: 3
Nested functions are useful for encapsulating functionality within a specific scope and promoting code
organization. They can help in creating reusable code blocks, implementing closures, and achieving better
modularity. The inner function can access the variables of the outer function, even after the outer function has
finished executing.
Note that the inner function is not accessible outside the scope of the outer function. It is confined within the
scope of the outer function, providing encapsulation and preventing naming conflicts with other parts of the
program.
Example:
def modify_number(num):
num += 1
x = 5
modify_number(x)
print("Outside function:", x)
Example:
def modify_list(lst):
lst.append(4)
my_list = [1, 2, 3]
modify_list(my_list)
It's important to note that even though mutable objects are passed by reference, reassigning the parameter
within the function will not affect the original object.
def reassign_list(lst):
lst = [4, 5, 6]
my_list = [1, 2, 3]
reassign_list(my_list)
When the function reassign_list is called with my_list, a new local variable lst is created within the scope of the
function, and it references a new list [4, 5, 6]. This reassignment statement inside the function creates a new list
object and points the local variable lst to that new object, but it doesn't affect the original list my_list outside
the function. The original list my_list remains unchanged outside the function, and the reassignment only
affects the local variable lst within the function's scope.
Important
In summary, Python uses a combination of pass by value and pass by reference depending on the object type:
Pass by Value: Immutable objects, such as numbers (integers, floats), strings, and tuples, are passed by value.
A copy of the object's value is created and assigned to a new local variable within the function. Changes made
to the local variable do not affect the original object outside the function.
Pass by Reference: Mutable objects, such as lists, dictionaries, and custom objects, are passed by reference.
The reference to the object is passed, not a copy of the object itself. Changes made to the mutable object
inside the function affect the original object outside the function.
To summarize, reassigning a parameter variable within a function creates a new local variable, regardless of
whether the object is mutable or immutable. The reassignment does not affect the original variable outside the
function's scope. If you want to modify the original object, you need to directly modify its contents instead of
reassigning the parameter variable.
A lambda function, also known as an anonymous function or a lambda expression, is a way to define small,
one-line functions without using the def keyword. Lambda functions are commonly used for simple, one-time
operations and are typically defined inline where they are needed.
Syntax:
add = lambda x, y: x + y
result = add(3, 5)
print(result) # Output: 8
In this example, lambda x, y: x + y defines a lambda function that takes two arguments x and y, and returns their
sum. The lambda function is assigned to the variable add, and then called with arguments 3 and 5. The
returned value, 8, is stored in the result variable and printed.
Built-in functions
Python provides a wide range of built-in functions that are readily available for use without requiring any
additional code or imports.
Question: Write a Python function to calculate the factorial of a number (a non-negative integer). The
function accepts the number as an argument.
Code: https://pastebin.com/tCwhDeSJ
Question: Write a Python function that takes a number as a parameter and checks whether the number is
prime or not.
Code: https://pastebin.com/LV5TavZw
x = 50
def func(x):
x = 2
func(x)
print('x is now', x)
(a) x is now 50
(b) x is now 2
(d) None
print(message * times)
say('Hello')
say('World', 5)
(a) Hello
WorldWorldWorldWorldWorld
(b) Hello
World 5
(c) Hello
World,World,World,World,World
(d) Hello
HelloHelloHelloHelloHello
WorldWorldWorldWorldWorld
Recursion
What is Recursion?
The process in which a function calls itself directly or indirectly is called recursion and the corresponding
function is called a recursive function.
def recurse():
recursive
call
recurse()
recurse()
Example:-
Code:- https://pastebin.com/mkn8VxK6
Self-Calling: The defining feature of a recursive function is that it calls itself within its body. This allows the
function to break down a complex problem into simpler subproblems of the same type
Base Case: To avoid infinite recursion, a recursive function must have a base case, which is the simplest
form of the problem that can be directly solved without further recursion. When the function encounters the
base case, it stops calling itself and starts returning values back through the call stack
Recursive Case: The recursive case is the part of the function where it calls itself with a modified (usually
smaller) version of the original problem. This recursive call allows the function to work towards the base case
by solving smaller subproblems and combining their results to get the final solution
Repetition and Replication: Recursive functions typically handle repetitive or replicated structures in a
problem. The function addresses the same type of problem multiple times with slightly different inputs until it
reaches the base case
Memory Usage: Recursive functions utilize the call stack to store the intermediate state of function calls.
Each time the function calls itself, a new stack frame is created, and when the base case is reached, the
stack frames are popped off as the functions return their results. Excessive recursion without a proper base
case can lead to stack overflow errors
Readability and Elegance: In some cases, recursive solutions can provide a more elegant and concise way
to express algorithms, especially for problems with clear repetitive patterns.
Base Case: The base case is the stopping condition of the recursive function. It is the simplest scenario
where the function does not call itself but instead returns a straightforward result. The base case is essential to
prevent infinite recursion and allows the recursion to terminate successfully.
function countdown(num) {
if (num <=0) {
console.log(“You’ve reached the end”);
Base Case
return;
}
console.log(num);
num--
countdown(num) Recursive function
}
Recursive Case: The recursive case is where the function calls itself to solve a smaller or simpler version
of the original problem. By breaking the problem down into smaller subproblems, each handled by the same
function, the recursive case aims to reach the base case eventually.
The call stack plays a critical role in managing recursive calls. It is a region of memory used by the program
to keep track of function calls and their local variables. When a function is called, its execution context (local
variables, parameters, and return address) is pushed onto the call stack. When the function returns, its
context is popped off the stack
For recursive functions, the call stack is particularly important because it allows the program to maintain
multiple active instances of the same function, each with its own set of local variables and arguments. This
way, the recursive calls can be nested and tracked until the base case is reached.
Example:-
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
As the recursive calls return and the call stack unwinds, the results are combined to compute the
factorial of the original number (5 in this case). The call stack efficiently manages the recursive
function calls, allowing the program to handle complex problems through the recursive approach.
Visualising Recursio
The topmost function call in the stack diagram is the one that is currently executing. In this case, it is the
factorial(3) function call. The next function call in the stack diagram is the factorial(2) function call. This
function call was made by the factorial(3) function call, and it is waiting to be executed. The remaining
function calls in the stack diagram are all waiting to be executed as well
The values of the function's parameters and local variables are also shown in the stack diagram. In this
case, the n parameter for each function call is the number that the function is currently calculating the
factorial of
As the recursive function executes, the stack diagram will grow. Each time the function calls itself, a new
function call will be added to the stack diagram. When the function reaches its base case, the function calls
will be popped off the stack in reverse order.
code:-https://pastebin.com/yyajDRPt
Explanation:-
The function print_n_to_1 recursively prints numbers from 'n' to 1 in descending order. It starts with 'n', prints it,
then calls itself with 'n-1' until 'n' becomes 1. If 'n' is not a positive integer, the program asks the user to input a
positive integer and then prints the sequence from the given 'n' down to 1.
Time Complexity: The time complexity of the print_n_to_1 function is O(n) because it recursively prints
numbers from 'n' to 1 in descending order. The function makes 'n' recursive calls before reaching the base case
where 'n' becomes 0, so the time complexity is linear, proportional to the value of 'n'.
Space Complexity: The space complexity of the print_n_to_1 function is O(n) due to the recursion. Each
recursive call adds a new stack frame to the call stack, and the maximum depth of the call stack is 'n'.
Therefore, the space complexity is linear, proportional to the value of 'n'.
Code:-https://pastebin.com/fLZC6Emr
Explanation:-
Time Complexity: The time complexity of the print_numbers_recursive function is O(n), where n is the input
number. Each recursive call reduces the value of n by 1, and since there are n total calls (from n to 1), the time
complexity grows linearly with the input.
Space Complexity: The space complexity of the print_numbers_recursive function is O(n) as well. This is because
the recursive calls are added to the call stack, and in the worst case, there will be n calls stacked up due to the
recursion.
Code:-https://pastebin.com/J13i7d49
Explanation:-
a. Check if n is equal to 1.
c. If n is not 1, calculate the sum of n and the result of a recursive call to calculate_sum_recursive(n - 1).
d. This adds up the current number n with the sum of numbers from 1 to n-1.
5. The recursive function calculates the sum by breaking down the problem of finding the sum of numbers from 1 to
6. The recursion continues until the base case is reached (when n becomes 1).
7. As the recursive calls unwind, the function adds up the numbers to compute the total sum.
Time Complexity: The time complexity of the calculate_sum_recursive function is O(n), where n is the input
number. Each recursive call reduces the value of n by 1, and there are n total calls (from n to 1) needed to calculate
the sum.
Space Complexity: The space complexity of the calculate_sum_recursive function is O(n) as well. This is because
each recursive call adds a new function call to the call stack, and in the worst case, there will be n calls stacked up
due to the recursion. Therefore, the space used by the call stack grows linearly with the input number n, resulting in
a linear space complexity.
Code:-https://pastebin.com/6UPs3Etc
Explanation:-
The factorial_recursive function calculates the factorial of a given number 'n' using recursion. If 'n' is 0 (the base
case), the function returns 1, as the factorial of 0 is defined to be 1. Otherwise, it returns 'n' multiplied by the factorial
of 'n - 1', effectively calculating the factorial of 'n' through a recursive chain of multiplications.
Time Complexity: The time complexity of the factorial_recursive function is O(n) because each recursive call
reduces 'n' by 1 until it reaches the base case where 'n' becomes 0. Therefore, the function makes 'n' recursive calls,
resulting in linear time complexity.
Space Complexity: The space complexity of the factorial_recursive function is O(n) as well because the recursion
stack will have 'n' frames at its peak depth. Each recursive call consumes space on the stack until the base case is
reached, at which point the stack frames are gradually removed, and the space is released.
Make a function that calculates ‘a’ raised to the power ‘b’ using recursion.
Code:-https://pastebin.com/qPjcbu1u
Explanation:
The power_recursive function calculates 'a' raised to the power 'b' using recursion. If the exponent 'b' is 0, it
returns 1 (base case).
Otherwise, it recursively multiplies 'a' with the result of power_recursive
(a, b -1), reducing 'b' by 1 in each recursive call. The code print(power_recursive(2,4)) calculates 2^4 using the
recursive function, which yields the result 16.
Time Complexity: The time complexity of the power_recursive function is O(b) because each recursive call
reduces the exponent 'b' by 1 until it reaches the base case where 'b' becomes 0. Therefore, the function makes 'b'
recursive calls, resulting in linear time complexity.
Space Complexity: The space complexity of the power_recursive function is O(b) as well because the recursion
stack will have 'b' frames at its peak depth. Each recursive call consumes space on the stack until the base case is
reached, at which point the stack frames are gradually removed, and the space is released.
Code:-https://pastebin.com/YumK4iRV
Explanation:-
The given Python code defines a function fibonacci_sequence(n_terms) that generates the Fibonacci sequence
up to the specified number of terms (n_terms). The function starts with the first two terms of the sequence (0 and
1) and continues adding new terms by summing the last two terms in the sequence. It does this in a while loop until
the desired number of terms is reached. Finally, it returns the generated Fibonacci sequence.
Time complexity: The time complexity of the fibonacci_sequence function is O(n), where 'n' is the number of terms
requested by the user. This is because the function uses a while loop to generate the Fibonacci sequence up to the
specified number of terms. Each iteration of the loop takes constant time, and the loop runs 'n' times, making the
overall time complexity linear.
Code:- https://pastebin.com/RB8jN80u
Explanation:-
The function fibonacci(n) calculates the nth Fibonacci number using recursion. The Fibonacci sequence is a
series of numbers in which each number is the sum of the two preceding ones, starting with 0 and 1.
Time complexity:
The time complexity of the fibonacci function is exponential, specifically O(2^n)
This is because, in the recursive implementation, each call to the fibonacci function results in two additional
recursive calls (for n - 1 and n - 2). As a result, the number of recursive calls increases exponentially with the
value of n.
Space complexity:-
In this code, fibonacci(7) will require a stack with 7 frames to calculate the 7th Fibonacci number, and thus the
space complexity is O(7), which is equivalent to O(n) in this case.
Pros of Recursion:
Clarity and Readability: Recursive solutions can often be more intuitive and easier to understand for
problems that have a clear repetitive structure or involve traversing hierarchical data structures (e.g., trees,
graphs)
Simplicity: Recursion can lead to more concise code compared to iterative solutions, as it simplifies the
process of breaking down a complex problem into smaller subproblems
Divide and Conquer: Recursive algorithms naturally follow the "divide and conquer" approach, breaking a
problem into smaller parts that are more manageable and easier to solve
Elegant Problem-Solving: In certain cases, recursion provides a more elegant way to express an algorithm,
making it easier to reason about and maintain
Handling Recursive Data Structures: When working with recursive data structures, such as linked lists or
trees, recursive functions often provide a natural and concise way to process or traverse them.
Cons of Recursion:
Stack Space Usage: Recursive calls consume memory on the call stack. If the recursion is too deep or
doesn't have a proper base case, it can lead to a stack overflow, causing the program to crash
Performance Overhead: Recursive calls often involve additional function call overhead and can be less
efficient than iterative solutions for certain problems, due to the constant creation and destruction of stack
frames
Debugging Complexity: Debugging recursive functions can be more challenging compared to iterative
code. If a base case is missing or incorrectly defined, it can lead to infinite recursion or incorrect results
Algorithmic Complexity: Some problems might be better suited for iterative solutions or other algorithms.
Recursion is not always the most efficient approach, especially when the problem doesn't naturally exhibit a
repetitive structure
Tail Recursion Optimization Limitations: In some programming languages, tail recursion optimization
might not be available or limited, leading to excessive stack space usage for tail-recursive functions
Code Tracing Difficulty: Tracing the flow of execution in recursive functions can be more complicated than
iterative code, which may make it harder to understand the program's behaviour during debugging.
Chapter-5
String in Python
In Python, strings are a sequence of characters enclosed within single (' '), double (" "), or triple (''' ''' or """ """)
quotes. They are used to represent text data and are one of the fundamental data types in Python. Strings are
immutable, which means once they are created, their content cannot be changed. However, you can create
new strings by manipulating existing ones.
Syntax
Strings can be created using single, double, or triple quotes. For example:
name = 'John'
For eg:
After assigning a string to a variable, you can use that variable throughout your code to access and
manipulate the string data.
For example, let's assign a string to a variable and then use it in a print statement:
You can also perform various operations on the string using the assigned variable:
name = "Alice"
Example:
print(text[0]) # Output: H
print(text[4]) # Output: o
print(text[-1]) # Output: !
We access individual characters in the string text using positive and negative indices. Positive indices start
from the beginning of the string, while negative indices start from the end.
Traversing a string
Traversing a string in Python means iterating through each character of the string one by one. It's useful when
you need to perform specific operations on each character, search for patterns, or count occurrences of
certain characters or substrings within the string.
https://pastebin.com/3rym6FQr
https://pastebin.com/Gv5n5hBt
https://pastebin.com/m3AE1dpe
https://pastebin.com/zW7pxLSi
String length
In Python, you can determine the length of a string using the built-in len() function. The len() function returns
the number of characters in the string, including spaces and special characters.
Example:
length = len(text)
print(length) # Output: 13
String find()
The find() method finds the first occurrence of the specified value. The find() method returns -1 if the value is
not found.
Code: https://pastebin.com/HdBYjbsS
Slicing a string
Slicing a string in Python allows you to extract a portion of the string by specifying the start and end indices.
The syntax for slicing is string [start:end], where start is the index of the first character to include in the slice
(inclusive), and end is the index of the first character to exclude from the slice (exclusive).
Note: Remember that slicing doesn't modify the original string; it creates a new string containing the specified
portion of the original string.
When slicing a string in Python, you can omit the start index to slice from the beginning of the string. If you don't
specify the start index, Python assumes that you want to start from the first character (index 0). This is known
as slicing from the start.
Code: https://pastebin.com/UmXEYPsB
When slicing a string in Python, you can omit the end index to slice until the end of the string. If you don't specify
the end index, Python assumes that you want to include all characters until the end. This is known as slicing till
the end.
Code: https://pastebin.com/aRtXRRrz
Example:
Code: https://pastebin.com/BWFUeGnc
Explanation
slice1 extracts characters from index 0 to index 4 (inclusive), giving us "Hello"
slice2 starts from index 7 and includes all characters until the end of the string, giving us "World!"
slice3 starts from the beginning of the string and goes up to index 4 (inclusive), giving us "Hello"
slice4 uses negative indexing to start from the 6th character from the end and includes all characters until
the end, giving us "World!"
slice5 uses a step of 2, which means it skips every other character from the slice, giving us "Hlo ol".
In Python, negative indexing allows you to access elements in a sequence (like strings, lists, tuples) from the
end of the sequence using negative numbers. The index -1 refers to the last element, -2 refers to the second-
to-last element, and so on.
Example
Code: https://pastebin.com/T3ZfSzza
Explanation: In the example above, -1 represents the last character in the string "Hello, World!" which is "!".
Similarly, -2 represents the second-to-last character which is "d". When using negative indexing with slicing,
the start and end indices are also counted from the end of the string. So, slice1 is "World" since it starts from
the sixth-to-last character ("W") and goes up to the second-to-last character ("d").
Modifying Strings
In Python, strings are immutable, which means you cannot modify them directly once they are created.
However, you can perform operations on strings to create new strings with the desired modifications. Since
you cannot change individual characters in a string directly, you can create new strings by combining or
slicing the original string, and then assign the modified string to a new variable.
upper()
upper() is a string method used to convert all the characters in a string to uppercase. It returns a new string
with all uppercase letters while leaving any non-alphabetic characters unchanged.
Code: https://pastebin.com/ThKj1JAi
lower()
The lower() is a string method used to convert all the characters in a string to lowercase. Just like the
upper() method, the lower() method does not modify the original string (text) but instead creates a new
string with all lowercase characters. This behavior is due to the immutability of strings in Python.
Code: https://pastebin.com/JtyiEMyE
strip()
strip() is a string method used to remove leading and trailing whitespace characters from a string. By
default, it removes spaces, tabs, and newline characters from the beginning and end of the string. It returns
a new string with the leading and trailing whitespace removed.
Code: https://pastebin.com/L4viAxRW
replace()
replace() is a string method used to replace occurrences of a substring with a new substring within a string.
It returns a new string with the replacements made, leaving the original string unchanged.
Syntax:
Code: https://pastebin.com/HAanKM0X
split() is a string method used to split a string into a list of substrings based on a specified delimiter. It allows
you to break a string into smaller parts whenever the delimiter is encountered. The split() method returns a
list of these substrings.
Syntax:
string.split(sep, maxsplit
sep (optional): The delimiter that specifies where the string should be split. If not provided, the default
delimiter is whitespace (spaces, tabs, and newlines)
maxsplit (optional): Specifies the maximum number of splits to be performed. If not provided, there is no
limit to the number of splits.
Code: https://pastebin.com/8QJHwwcM
capitalize()
capitalize() is a string method used to capitalize the first character of a string. It converts the first character
to uppercase and leaves the rest of the characters unchanged. If the first character is already in uppercase,
the method does not alter it.
Code: https://pastebin.com/cpYeB0UU
Concatenation
Concatenation in Python refers to the process of combining two or more strings together to form a single
string. This can be achieved using the + operator or by using string formatting methods like str.format() or f-
strings (formatted string literals).
Code: https://pastebin.com/WCaBWM04
format()
format() is a string method used for string formatting. It allows you to insert values into a string in a
structured and controlled way. The format() method uses curly braces {} as placeholders, which are
replaced with the values passed as arguments to the format() method.
Code: https://pastebin.com/dy4JzzES
Question:
quantity = 3
item_number = 567
price = 49.95
Replace the variables in this string with their values and print the output string: I want quantity pieces of
item_number item for price dollars.
Code: https://pastebin.com/nixS1Ygq
Escape characters
Escape characters are special characters that are used to represent certain non-printable or reserved
characters within a string. They are represented by a backslash (\) followed by a character or a
combination of characters. Escape characters enable you to include characters in a string that would
otherwise be difficult or impossible to represent directly.
Question:
Code: https://pastebin.com/NTJCDaHE
Question: Write a Python function that checks if the given string is a palindrome or not.
Input: mama
Output: True
Code: https://pastebin.com/iR2xRCmT
Question: Write a Python function that replaces all commas with dots and all dots with commas in the
given string.
Code: https://pastebin.com/KgUKXt7M
c. Error
d. Hello and
str1="6/4"
print("str1")
a. 1
b. 6/4
c. 1.5
d. str1
str1="Information"
print(str1[2:8])
a. format
b. formatio
c. orma
d. ormat
str1="Application"
str2=str1.replace('a','A')
print(str2)
a. application
b. Application
c. ApplicAtion
d. applicAtion
str1="poWer"
str1.upper()
print(str1)
a. POWER
b. Power
c. power
d. poWer
Answers
a) Hello foo and bi
d) str
a) forma
c) ApplicAtio
d) poWer
Introduction to OOPs
Object-Oriented Programming (OOP) is a way of organizing and designing computer programs to make them
more organized, easier to understand, and reusable. At its core, OOP is about modeling real-world objects and
their interactions in a computer program.
Using Object-Oriented Programming in Python provides numerous advantages, including code reusability,
organization, and maintainability. By leveraging OOP principles, you can design robust and flexible software
systems that mimic real-world entities and interactions, making it easier to develop and maintain complex
projects.
Classes:
In OOP, Classes are like user-defined data types, allowing you to encapsulate data (attributes) and operations
(methods) that are related to a specific concept or entity. A class is defined using the class keyword in Python.
For example, if you were creating a program for managing cars, you could define a "Car" class. This class would
specify attributes like "make," "model," "color," and methods like "accelerate" and "brake."
Objects:
An object is an instance of a class. It represents a specific occurrence of the class and contains the actual
data defined in the class's attributes. In other words, an object is a concrete representation of the abstract
blueprint provided by the class.
Example: An object of the "Car" class could be a particular car, such as an Audi, Nissan, and Volvo. Each car
object would have its own unique attribute values, separate from other car objects created from the same
class.
Objects
In a class, you can have both functions (methods) and variables (attributes). Functions in a class are methods,
and they define the behavior or actions that objects of that class can perform. Variables in a class are
attributes, and they represent the data associated with the objects of that class.
Attributes:
Class attributes are shared among all instances (objects) of the class. They are defined within the class,
outside any method, and are the same for all objects. Class variables are accessed using the class name
Methods:
Methods are functions defined within the class. They operate on the attributes and can perform various
actions. Methods have self as their first parameter, representing the object calling the method.
Once you have created an object, you can call its methods and access its attributes using dot notation
The four pillars of Object-Oriented Programming (OOP) are the four fundamental principles that guide the
Encapsulation:
Encapsulation is the principle of bundling data (attributes) and methods (functions) that operate on that data
within a single unit called a class. It allows the class to control the access to its internal data, ensuring that the
Abstraction:
Abstraction is the process of simplifying complex systems by representing the relevant characteristics and
behaviors without exposing the underlying implementation details. In OOP, abstraction is achieved by creating
abstract classes or interfaces that define the structure and behavior of objects without specifying how those
Abstraction allows developers to focus on what an object does rather than how it does it.
Inheritance:
Inheritance is the ability of a class to inherit attributes and methods from its parent or base class. It allows you
to create a hierarchy of classes, where subclasses (child classes) can inherit and extend the functionality of
Polymorphism means "many forms" and refers to the ability of objects to take on multiple forms or be treated
as instances of their parent class. Polymorphism simplifies code by promoting generic programming and
supporting the principle of "write once, use anywhere."
In simpler terms, polymorphism allows you to use a single method or function name to perform different
operations based on the context or type of data it operates on.
In Real Life...
Abstraction Encapsulation
Inhertance Polymorphism
Defining a Class
A class in Python involves creating a blueprint or template for creating objects of that class. It defines the
structure, attributes, and behaviors of objects that will be instantiated from the class.
Syntax:
class ClassName:
class_attribute1 = value1
class_attribute2 = value2
class ClassName:
This is the beginning of the class definition, where ClassName is the name of the class. The class body,
containing class attributes and methods, is indented below this line.
Inside the class body, you can define class attributes (variables) if needed. Class attributes are shared among
all instances (objects) created from the class and are accessed using the class name (ClassName.attribute).
You can define class methods (functions) within the class to specify the behavior of the class. Class methods
are similar to regular functions, but they operate on the class itself and can access class attributes. Class
methods have the self parameter as the first parameter to access class attributes and other methods.
Instantiating Object
It involves creating instances of a class using the class name followed by parentheses. When you create an
instance of a class, you are creating a new object that is based on the blueprint provided by the class.
Syntax:
object_name = ClassName()
object_name: This is the name you choose for the object, which is a reference to the instance of the class
you are creating
ClassName: This is the name of the class you want to instantiate. It should be a valid Python identifier.
Once you have created an object, you can use it to access the class's attributes and methods.
Instance attributes
Instance attributes are variables or data members that belong to individual instances of a class in object-
oriented programming. Each object created from a class has its own set of instance attributes, which can hold
unique values or states specific to that particular instance. These attributes define the characteristics and
properties of the object and can be accessed and manipulated using the instance of the class. Instance
attributes are typically initialized within the class's constructor method and are used to represent the varying
characteristics or data associated with each object.
VariableName3 Type
Instantiate
Class attributes and instance attributes are fundamental concepts in object-oriented programming that
define the characteristics and behavior of objects within a class. They serve distinct roles in defining the
structure and behavior of objects.
Example:
Write a Python class named Rectangle to represent a rectangle shape. The class should have the following
functionalities
A method named set_dimensions that takes two parameters width and height and sets the attributes of
the rectangle object accordingly
A method named area that calculates and returns the area of the rectangle
A method named perimeter that calculates and returns the perimeter of the rectangle
Use this to create objects of the class and print the width, height, area and perimeter.
Code: https://pastebin.com/bQ4WLJkY
Explanation
We define the Rectangle class where we have a method named set_dimensions() that takes width and
height as arguments and sets the instance variables' width and height accordingly
The area() and perimeter() methods calculate the area and perimeter of the rectangle based on its
attributes
We create an object rectangle1 of the Rectangle class
We set the dimensions of rectangle1 using the set_dimensions() method
We access the attributes of rectangle1 (i.e., width and height) using dot notation and call its methods (i.e.,
area() and perimeter()) to perform calculations.
Class constructors in Python are special methods that are automatically called when you create an instance
(object) of a class. The constructor method is denoted by __init__, and it allows you to initialize the attributes
of the object with values passed as arguments during instantiation. Constructors are essential for setting up
the initial state of the object, ensuring that it is in a valid and usable state when it is created.
Syntax:
class ClassName:
This is the special constructor method for the class. The __init__ method is automatically called when you
create an instance of the class. The first parameter self refers to the instance of the object being created, and it
allows you to access and modify the object's attributes
These are the parameters of the constructor that you expect to receive when creating an object. They
represent the initial values that you want to assign to the object's attributes.
Inside the constructor, you can use the self parameter to set the initial state of the object by assigning values
to its attributes. This way, each instance of the class can have its own unique attribute values.
Example:
Create a Python class named Person to represent a person's information. The class should have the following
features
A constructor __init__ that takes two parameters name and age and initializes the attributes of the person
object accordingly
A method named say_hello that prints a greeting message, displaying the person's name and age.
Once you have defined the Person class, create two objects to demonstrate the working.
Code: https://pastebin.com/edS8Tf7B
In this example, the Person class has a constructor that takes name and age as parameters. During object
creation (person1 and person2), we pass the corresponding arguments, and the constructor initializes the
name and age attributes of the objects with those values. The say_hello() method then uses the attribute
values to display a greeting for each person object.
Access Modifiers
In Python, access modifiers are used to control the visibility and accessibility of class attributes and methods.
They determine how the attributes and methods can be accessed from outside the class or within the class
hierarchy. Python provides three types of access modifiers
Publi
Protecte
Private
In Python, there is no strict implementation of public access modifiers. By default, all attributes and methods
defined in a class are considered public, and they can be accessed from both inside and outside the class.
Syntax:
class MyClass:
def public_method(self):
pass
def __init__(self):
Both public_method() and public_attribute are accessible from outside the class.
In Python, an attribute or method prefixed with a single underscore _ is considered protected, but it is still
accessible from outside the class. However, it is a convention that indicates the attribute or method is intended
for internal use within the class or its subclasses and should not be accessed directly from outside the class.
class MyClass:
def _protected_method(self):
pass
def __init__(self):
Although _protected_method() and _protected_attribute can be accessed from outside the class, it is
recommended to use them only within the class or its subclasses.
In Python, an attribute or method prefixed with a double underscore __ is considered private. It is not directly
accessible from outside the class, and attempting to access it will raise an AttributeError.
class MyClass:
def __private_method(self):
pass
def __init__(self):
Both __private_method() and __private_attribute cannot be accessed directly from outside the class.
Getter and setter methods, also known as accessor and mutator methods, are used in object-oriented
programming to control access to the attributes of a class. They provide a way to get (retrieve) and set
(modify) the values of private attributes indirectly, promoting encapsulation and data hiding.
A getter method is used to retrieve the value of a private attribute. It is decorated with @property, and its
name should be the same as the name of the attribute you want to get but without the leading underscore
(for convention).
class MyClass:
def __init__(self):
self._private_attribute = 0
@property
def private_attribute(self):
return self._private_attribute
In this example, the private_attribute is a private attribute with a leading underscore _. The private_attribute
getter method allows you to access this private attribute's value without exposing it directly.
Setter Method:
A setter method is used to modify the value of a private attribute. It is decorated with
@<attribute_name>.setter, and its name should be the same as the name of the attribute you want to set but
without the leading underscore (for convention).
class MyClass:
def __init__(self):
self._private_attribute = 0
@property
def private_attribute(self):
return self._private_attribute
@private_attribute.setter
self._private_attribute = value
In this example, we have defined a setter method for private_attribute, which allows you to set a new value to
the _private_attribute indirectly.
Now, we can use these getter and setter methods to access and modify the private attribute:
obj = MyClass()
print(obj.private_attribute) # Output: 0
obj.private_attribute = 42
print(obj.private_attribute) # Output: 42
By using getter and setter methods, you can control how the private attributes are accessed and modified,
and you can add additional logic or validation before allowing any changes to the attributes. This helps to
maintain the integrity of the data and ensures that the class's internal state remains consistent.
Inheritance
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a new class (called
the subclass or derived class) to acquire the properties and behaviors of an existing class (called the
superclass or base class). The subclass can reuse and extend the functionality of the superclass, promoting
code reuse and creating a hierarchical relationship between classes.
class SuperClass:
class SubClass(SuperClass):
SuperClass:
This is the existing class (base class) from which you want to inherit attributes and methods.
SubClass:
This is the new class (derived class) that will inherit from the superclass. You define the subclass name,
followed by the superclass name inside parentheses, to indicate inheritance.
Example:
Code: https://pastebin.com/KNT4HskK
In this example, we have a base class Animal with an __init__ method setting the species attribute and a
make_sound method. Two subclasses, Dog and Cat, inherit from Animal. They override the make_sound
method to provide their own sounds. Inheritance allows reusing and extending functionality, and instances of
Dog and Cat inherit the species attribute and methods from the base class.
Types of Inheritance
Inheritance in object-oriented programming can be classified into several types based on the relationship
between the classes involved. The main types of inheritance are:
Single Inheritance:
Single inheritance involves one class (subclass) inheriting from a single superclass. In other words, a subclass
can have only one direct parent class. It forms a linear hierarchy. Single inheritance is the simplest and most
commonly used form of inheritance.
Syntax:
class SuperClass:
pass
class SubClass(SuperClass):
pass
B Single Inheritance
Multiple inheritance involves one class inheriting from multiple superclasses. The subclass will have features
from all the superclasses. Python supports multiple inheritance.
Syntax:
class SuperClass1:
pass
class SuperClass2:
pass
pass
A B
C
Multiple Inheritance
Multilevel Inheritance:
Multilevel inheritance involves one class inheriting from another class, and then another class inheriting from
the second class. It creates a chain of inheritance.
Syntax:
class Grandparent:
pass
class Parent(Grandparent):
pass
class Child(Parent):
pass
Base class A
Intermediary B
class
Derived class C
Hierarchical inheritance involves one class (subclass) inheriting from a single superclass, while there are other
subclasses that inherit from the same superclass. It forms a tree-like structure.
Syntax:
class SuperClass:
pass
class SubClass1(SuperClass):
pass
class SubClass2(SuperClass):
pass
Hybrid inheritance involves a combination of multiple and multilevel inheritance. It can occur when a class
inherits from multiple classes and also has subclasses inheriting from it.
Syntax:
class SuperClass1:
pass
class SuperClass2:
pass
pass
class SubClass2(SubClass1):
pass
Base Class
Derived Class 3
The "diamond problem" is a term used to describe an ambiguity that can occur in object-oriented
programming languages with multiple inheritance and a certain type of class hierarchy structure.
Class A
Class B Class C
Class D
Now, imagine that classes B and C both have a method with the same name. When a method from class D
tries to access that method, there's ambiguity because D inherits from both B and C, which both provide the
same method through their common parent A.
Diamond Problem: This ambiguity is referred to as the "diamond problem" because the class hierarchy
looks like a diamond shape when visualized.
Solution:
The diamond problem in Python is automatically solved by the Method Resolution Order (MRO) mechanism.
Python uses the C3 Linearization algorithm to determine the order in which base classes are checked for
method resolution. This mechanism ensures that the diamond problem is resolved without any extra steps
required from the programmer.
Code: https://pastebin.com/tgKjFqS6
Polymorphism
Polymorphism is a core concept in object-oriented programming (OOP) that allows objects of different
classes to be treated as objects of a common superclass. It enables a single interface to represent different
data types or objects, providing flexibility and reusability in code.
Example:
In this example, the base class Animal has an abstract method speak(), meant to be overridden by its
subclasses Dog, Cat, and Cow, which inherit from Animal. When instances of these subclasses call the speak()
method, polymorphism allows the same method name to be used for different objects, invoking the
appropriate implementation based on the object's type. This uniform treatment of different classes enables
code reuse and flexibility in handling diverse behaviors.
This type of polymorphism is resolved during compile-time. It is achieved through method overloading or
operator overloading. Method overloading allows a class to have multiple methods with the same name but
different parameters, while operator overloading allows defining custom behaviors for operators such as +, -,
*, etc., for objects of a class.
# Method overloading
class MyClass:
return a + b
return a + b + c
# Operator overloading
class ComplexNumber:
self.real = real
self.imag = imag
This type of polymorphism is resolved during run-time. It is achieved through method overriding. Method
overriding occurs when a subclass provides a specific implementation of a method that is already defined in
class Shape:
def draw(self):
class Circle(Shape):
def draw(self):
class Square(Shape):
def draw(self):
circle = Circle()
square = Square()
Polymorphism allows code to be more generic, as a single interface can handle different types of objects,
providing flexibility and extensibility. It promotes code reuse, simplifies maintenance, and enables the
implementation of abstract behaviors through interfaces or base classes.
Abstraction
Abstraction is a fundamental principle in object-oriented programming (OOP) that allows you to create a
simplified representation of real-world objects by focusing on their essential characteristics while hiding
unnecessary details. It is the process of defining the essential features of an object or class, abstracting away
the implementation complexities, and providing a clear and simplified interface for the user.
Example:
Code: https://pastebin.com/H6iv9qCT
Encapsulation
Encapsulation is a fundamental principle in object-oriented programming (OOP) that bundles data (attributes)
and the methods (functions) that operate on that data within a single unit called a class. It is the concept of
wrapping the data and methods together to hide the internal details of how the class works from the outside
world. The encapsulated data can only be accessed and modified through the methods defined in the class,
which act as a protective barrier or interface.
Example:
Code: https://pastebin.com/Nwefqq1L
The BankAccount class encapsulates account number and balance attributes. deposit() and withdraw()
methods provide controlled access to the private __balance attribute, ensuring data integrity. Python uses
name mangling (_BankAccount__balance) to make it less accessible but still private. It should not be
accessed directly outside the class.
Exception handling in object-oriented programming (OOP) is the process of dealing with unexpected or
exceptional situations that may occur during the execution of code within classes and objects. In Python,
exception handling is achieved using the try, except, and optionally finally blocks.
try:
except ExceptionType:
finally:
Explanation
try: The "try" block contains the code that you want to execute, which might raise an exception. If an exception
occurs within this block, the control will immediately move to the corresponding "except" block. If no exception
occurs, the "except" block will be skipped.
ExceptionType: This is the type of exception that you want to catch and handle. You can specify the specific
exception type you want to catch, such as "ValueError," "TypeError," "ZeroDivisionError," or use the more general
"Exception" to catch any exception.
except: The "except" block contains the code that will be executed if an exception of the specified type occurs in
the "try" block. If no exception occurs, the "except" block will be skipped.
finally: The "finally" block contains code that will always be executed, whether an exception occurred or not. It is
commonly used for cleanup operations, such as closing files or releasing resources.
Example: Implement exception handling in python by showing division operation. You can show exception -
“ZeroDivisionError”
Input:
a = 10
b = 2
Output:
Input:
a = 5
b = 0
Output:
Code: https://pastebin.com/Ruc7UWuH
In this example, the divide_numbers() function attempts to divide a by b. If b is zero, it will raise a
ZeroDivisionError, and the control will move to the except block, which prints an error message and assigns
None to the result. Regardless of whether an exception occurred or not, the finally block will be executed,
printing a cleanup message.
MC
What is the purpose of the "init" method in a class in Python?
b) It is automatically called when an object is created and allows you to initialize its attributes.
c) It is used to define class variables that are accessible by all instances of the class.
a) Polymorphism
b) Encapsulation
c) Abstraction
d) Inheritance
d) An abstract class may contain abstract methods, which must be implemented by its concrete subclasses.
4. What is the purpose of the "super()" function in Python when working with inheritance?
b) It allows the child class to override the methods of the base class.
Answer
b) It is automatically called when an object is created and allows you to initialize its attributes
d) Inheritanc
d) An abstract class may contain abstract methods, which must be implemented by its concrete
subclasses
a) It calls the constructor of the base class.
Practice Questions
Create a Python class to store and manage patient information in a hospital. Implement the Patient class with
encapsulation to ensure that sensitive information, such as the patient's medical history, is hidden from direct
access. The class should have methods to set and retrieve the patient's name, age, and contact information
while keeping the medical history private.
Example:
Patient Age: 35
Code: https://pastebin.com/mNGCRJDR
Explanation
Private attributes (__name, __age, __contact, and __medical_history) are marked with double
underscores, making them inaccessible from outside the class
Setter methods (set_name, set_age, set_contact) are used to modify the private attributes safely from
within the class.
4. The medical history (__medical_history) is kept private and can only be updated using the
add_medical_record method.
5. Encapsulation ensures data protection and restricts direct manipulation of sensitive information, promoting
Time Complexity:
The time complexity of the Patient class methods is generally O(1) since most of the methods involve simple
attribute assignments or return operations. The operations performed in the methods, such as setting and
retrieving patient details and medical history, do not depend on the size of any input data.
Space Complexity:
The space complexity of the Patient class is O(1) for each instance of the class. The class attributes (__name,
__age, __contact, and __medical_history) have fixed memory requirements for each instance, regardless of
the number of medical records. Additionally, the methods do not use any auxiliary data structures or recursive
calls that would increase the space usage with input size.
Question on Inheritance
Create a Bus child class that inherits from the Vehicle class. The default fare charge of any vehicle is seating
capacity * 100. If Vehicle is Bus instance, we need to add an extra 10% on full fare as a maintenance charge. So
total fare for bus instance will become the final amount = total fare + 10% of the total fare.
Example:
Code: https://pastebin.com/M0UG2019
Explanation
Vehicle Class: The Vehicle class is the parent class, and it has an instance variable seating_capacity that
represents the seating capacity of the vehicle. The class has a method fare that calculates the fare based
on the seating capacity, which is simply the seating capacity multiplied by 100
Bus Class: The Bus class is a child class that inherits from the Vehicle class. It has its constructor __init__
that calls the constructor of the parent class (Vehicle) using super().__init__(seating_capacity) to set the
seating capacity of the bus
The Bus class overrides the fare method of the parent class to calculate the fare for a bus instance. It first
calls the parent class's fare method to get the base fare based on the seating capacity. Then, it calculates a
10% maintenance charge based on the base fare and adds it to the base fare to get the final fare for the bus.
This final fare with the maintenance charge is then returned
The Bus class extends the functionality of the Vehicle class by adding a maintenance charge to the base
fare for bus instances, as required
Example Usage: In the example usage section, two instances are created, one of the Vehicle class and one
of the Bus class, with a seating capacity of 50.
a. The first instance vehicle of the Vehicle class has a seating capacity of 50. The fare is calculated as 50
b. The second instance bus of the Bus class also has a seating capacity of 50. The fare calculation
involves the fare method of the parent class, which calculates the total fare as 50 * 100 = 5000. Then,
a 10% maintenance charge is added (5000 * 0.1 = 500), resulting in a final_fare of 5000 + 500 = 5500.
Space Complexity
The space complexity of both classes is O(1) because they only have instance variables
(self.seating_capacity) and local variables (total_fare and maintenance_charge), which occupy a
constant amount of memory regardless of the input size.
Question:
Define two classes, India and USA. Each class has three methods: capital, language, and type. These methods
print out information about the capital, language, and type of the country, respectively to demonstrate
polymorphism.
The code then creates two objects, obj_ind and obj_usa, which are instances of the India and USA classes,
respectively. The code then iterates through a tuple of these objects and calls the capital(), language(), and
type() methods on each object.
Code: https://pastebin.com/QFXtfS06
Explanation
The India and USA classes both have methods with the same names: capital(), language(), and type(). This
is the basis for polymorphism
When the for loop iterates over the objects obj_ind and obj_usa, it treats them as instances of the same
base class (in this case, no explicit base class is defined, so they are treated as instances of the implicit base
class object)
During runtime, the appropriate method is dynamically selected based on the actual type of the object
being referred to at each iteration. This process is called method overriding
As a result, when the country.capital(), country.language(), and country.type() calls are made inside the
loop, the corresponding method of the specific class (either India or USA) is executed, displaying the
appropriate output for each country.
Time Complexity:
Each method in both the India and USA classes involves only print statements, which are basic operations with
a time complexity of O(1). The for loop iterates over two objects, one from each class, so the time complexity of
the loop is O(2) or simply O(1) since it's a constant number of iterations.
Space Complexity:
The space complexity of the classes is O(1) since they only contain methods with print statements and do not
use any additional data structures or variables that scale with the input.