0% found this document useful (0 votes)
4 views

Python Fullstack Visvodaya

The document provides a comprehensive introduction to Python, covering its features, history, and applications. It highlights Python's simplicity, readability, and versatility, making it suitable for various domains such as web development, data science, and automation. Additionally, it outlines the installation process, data types, comments, and operators in Python, emphasizing its community support and extensive libraries.

Uploaded by

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

Python Fullstack Visvodaya

The document provides a comprehensive introduction to Python, covering its features, history, and applications. It highlights Python's simplicity, readability, and versatility, making it suitable for various domains such as web development, data science, and automation. Additionally, it outlines the installation process, data types, comments, and operators in Python, emphasizing its community support and extensive libraries.

Uploaded by

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

1.

introduction to python

1.1 Python Overview

Python is a high-level, interpreted, and general-purpose programming language known for its
simplicity, readability, and broad applicability. Created by Guido van Rossum and first released
in 1991, Python emphasizes code readability and allows developers to express concepts in fewer
lines of code compared to other programming languages like Java or C++.

Key Features of Python

1. Simple and Easy to Learn


Python has a clean and easy-to-understand syntax that mimics natural language, making
it beginner-friendly.
2. Interpreted Language
Python code is executed line by line, which simplifies debugging and makes it easier to
test applications.
3. Dynamically Typed
Variables in Python do not require explicit declaration of data types, allowing for
flexible and efficient coding.
4. Extensive Standard Library
Python provides a rich set of built-in modules and functions, reducing the need for
external libraries for common tasks

Advantages of Python

• Cross-Platform Compatibility: Python programs can run on various operating systems


without modification.
• Large Community Support: A vast global community contributes to its development
and offers extensive support through documentation and forums.
• Integration Capabilities: Easily integrates with other languages and technologies such
as C, C++, Java, and RESTful APIs.
• Ideal for Rapid Prototyping: Python's simplicity allows developers to build and iterate
prototypes quickly.

Applications of Python

• Web Development: Using frameworks like Django, Flask, and FastAPI.


• Data Science and Analytics: With libraries like NumPy, pandas, and Matplotlib.
• Machine Learning and AI: Leveraging TensorFlow, Keras, and scikit-learn.
1 of 107
• Automation and Scripting: Automating repetitive tasks and writing scripts for system
administration.
• Game Development, GUI Applications, and More.

Popular Python Frameworks and Tools

• Web: Django, Flask, FastAPI


• Testing: PyTest, unittest
• Database: SQLAlchemy, Django ORM
• Front-End Integration: React, Angular (via REST APIs)
• Version Control & Deployment: Git, Docker, Heroku

1.2 Python History and Versions

1. Introduction to Python History

Python is a high-level, open-source programming language created by Guido van Rossum in the
late 1980s. Officially released in 1991, Python was designed to be simple, readable, and
powerful, drawing inspiration from the ABC language and influenced by Modula-3, C, and Unix
shell scripting. It has evolved into one of the most widely used languages across various domains
including web development, data science, automation, and artificial intelligence.

Timeline of Python Versions

Python 1.x Series

• Python 1.0 (January 1991)


o First official release.
o Included features like functions, modules, error handling, and core data types
(lists, strings, dictionaries).
• Key Feature Highlights:
o Introduction of object-oriented programming.
o Basic modules and standard library support.

Python 2.x Series

• Python 2.0 (October 2000)


o Introduced list comprehensions, garbage collection via reference counting, and
Unicode support.
2 of 107
o Widely adopted for many years but had some inconsistencies in language design.
• Notable Features:
o Improved memory management.
o Better support for object-oriented programming.
o Many libraries were built around Python 2, which delayed migration to Python 3.
• End of Life: Official support ended on January 1, 2020, marking a full shift to Python 3.

Python 3.x Series

• Python 3.0 (December 2008)


o A complete redesign of the language with incompatibility from Python 2.x.
o Focused on removing legacy constructs and ensuring consistency and
performance.
• Key Features Introduced in 3.x:
o print() as a function.
o Better Unicode support.
o New syntax for exception handling.
o New-style classes by default.
o Dictionary and range objects improved.
• Python 3.5 (2015) – Introduced async/await syntax for asynchronous programming.
• Python 3.6 (2016) – Introduced f-strings, type hints, and improved dictionaries.
• Python 3.7 (2018) – Performance improvements, data classes.
• Python 3.8 (2019) – Added the walrus operator (:=) for assignment expressions.
• Python 3.9 (2020) – New string methods, dictionary merges, and typing enhancements.
• Python 3.10 (2021) – Introduced pattern matching and better error messages.
• Python 3.11 (2022) – Focused on speed optimization (10–60% faster).
• Python 3.12 (2023) – Improved type annotations and performance tuning.
• Python 3.13 (Expected 2024/2025) – Focus on internal simplifications and even faster
execution.

3. Summary Table of Major Versions

Version Release Year Key Highlights


1.0 1991 Initial release with basic OOP features
2.0 2000 Unicode, garbage collection, list comps
3.0 2008 Incompatible upgrade, better Unicode
3.5 2015 async/await support
3.6 2016 f-strings, type annotations

3 of 107
3.10 2021 Structural pattern matching
3.11 2022 Major performance boost
3.12+ 2023+ Further speed, typing, and internal updates

4. Conclusion

Python has grown from a simple scripting language into a robust, multi-paradigm language
embraced across industries. The transition from Python 2 to Python 3 marked a major shift in
design philosophy, making Python future-ready and developer-friendly. With continuous updates
and community-driven development, Python remains a top choice for modern full stack
development.

1.3 Python Features

Python is known for its simplicity, versatility, and wide adoption in various fields like web
development, data science, artificial intelligence, automation, and more. Its features are designed
to enhance developer productivity and code maintainability.

1. Simple and Easy to Learn

• Python has a clean and readable syntax that mimics natural language.
• It reduces the learning curve for beginners and increases development speed for
experienced programmers.
• Example:

print("Hello, World!")

2. Interpreted Language

• Python is an interpreted language, meaning code is executed line-by-line by the Python


interpreter.
• This allows for easier debugging and rapid prototyping without the need for compilation.

3. Dynamically Typed

• Variables in Python do not require explicit declaration of type.


• The type is inferred at runtime based on the value assigned.

x = 10 # Integer
x = "Hi" # Now a String

4 of 107
4. Object-Oriented Programming (OOP)

• Python supports OOP principles like inheritance, encapsulation, and polymorphism.


• Everything in Python is treated as an object, promoting code modularity and reusability.

class Car:
def __init__(self, brand):
self.brand = brand

5. Extensive Standard Library

• Python comes with a large collection of built-in modules and functions.


• These libraries handle tasks like file operations, regular expressions, unit testing,
threading, and more.

import math
print(math.sqrt(16)) # Outputs: 4.0

6. Cross-Platform Compatibility

• Python is platform-independent and can run on Windows, macOS, Linux, and more
without code modification.
• It enhances portability for developers building cross-platform applications.

7. Free and Open Source

• Python is developed under an open-source license and is freely available to use,


distribute, and modify.
• A strong global community contributes to its continuous improvement.

8. High-Level Language

• Python handles memory management, garbage collection, and other low-level tasks,
allowing developers to focus on solving problems rather than managing system-level
details.

9. Embeddable and Extensible

• Python code can be embedded in other languages like C/C++.


• You can also use Python modules in other applications, making it highly integrable.

5 of 107
10. Rich Third-Party Ecosystem

• Python has a vast ecosystem of third-party libraries and frameworks for web (Django,
Flask), data science (NumPy, pandas), automation (Selenium), and machine learning
(TensorFlow, scikit-learn).

11. Support for Multiple Programming Paradigms

• Python supports:
o Procedural programming
o Object-oriented programming
o Functional programming (using lambda, map, filter, etc.)

12. Large Community and Strong Support

• Python has one of the largest developer communities in the world.


• Extensive online resources, documentation, tutorials, and active forums support learning
and troubleshooting.

Conclusion

Python’s feature set makes it a powerful and flexible tool for building modern software
solutions. Its simplicity, combined with robust capabilities, makes it the preferred language for
developers across industries.

1.4 Setting Up Python

🔹 1. Download Python

• Visit the official Python website: https://www.python.org/downloads/


• Choose the latest stable version (e.g., Python 3.12.x)
• Select the appropriate installer for your operating system:
 .exe for Windows
 .pkg for macOS
 Source files for Linux (or use package manager)

6 of 107
🔹 2. Install Python

Windows:

1. Run the downloaded installer.


2. Important: Check the box "Add Python to PATH" before clicking "Install
Now".
3. Complete installation and verify in Command Prompt:

bash
python --version

macOS:

1. Open the .pkg installer and follow the instructions.


2. To verify:

bash
python3 --version

Linux (Debian/Ubuntu-based):

1. Use the terminal to install:

bash
sudo apt update
sudo apt install python3

2. Verify:

bash
python3 --version

🔹 3. Install pip (Python Package Manager)

• Comes pre-installed with Python 3.4+

7 of 107
• Verify installation:

bash
pip --version

🔹 4. Install an IDE or Code Editor

Recommended editors:

• VS Code – Lightweight, feature-rich (https://code.visualstudio.com/)


• PyCharm – Full-featured IDE for Python
• Jupyter Notebook – Great for data science and education

🔹 5. Write and Run Your First Program

1. Open any text editor or IDE.


2. Create a file named hello.py and add:

print("Hello, Python!")

3.Run from terminal or command prompt:

bash
python hello.py

Python Identifiers
Identifiers in Python are the names used to identify variables, functions,
classes, modules, and other objects. They are fundamental elements in Python
programming, allowing developers to reference data and operations.

🔹 Rules for Valid Identifiers:

1. Can contain:
a. Letters (A–Z, a–z)
b. Digits (0–9)
c. Underscore (_)
2. Must begin with:
8 of 107
a. A letter or an underscore (_)
b. Cannot begin with a digit
3. Case-sensitive:
a. Name, name, and NAME are all different identifiers
4. Cannot be a keyword:
a. Reserved words like class, if, return, etc., cannot be used as identifiers

🔹 Examples of Valid and Invalid Identifiers:

Valid Invalid
user_name 2username
_temp user-name
Age class (keyword)

Python Data Types


In Python, data types define the kind of value a variable can hold. Python is dynamically typed,
meaning the interpreter automatically assigns the data type based on the value.

🔹 Built-in Data Types in Python

Python's standard data types are categorized as follows:

1. Numeric Types

• int – Integer values (e.g., 10, -5)


• float – Floating-point numbers (e.g., 3.14, -0.5)
• complex – Complex numbers (e.g., 2 + 3j)

2. Sequence Types

• str – String (text) values (e.g., "Hello")


• list – Ordered, mutable collection (e.g., [1, 2, 3])
• tuple – Ordered, immutable collection (e.g., (1, 2, 3))

3. Set Types

• set – Unordered, mutable collection of unique items (e.g., {1, 2, 3})


9 of 107
• frozenset – Immutable version of a set

4. Mapping Type

• dict – Key-value pairs (e.g., {"name": "Alice", "age": 25})

5. Boolean Type

• bool – Represents truth values: True or False

6. Binary Types

• bytes – Immutable sequence of bytes


• bytearray – Mutable sequence of bytes
• memoryview – Memory view object over bytes or bytearray

Python Data Types Summary Table

Category Type Example Description


Numeric int 100, -20 Integer numbers
float 3.14, -0.5 Decimal numbers

complex 2 + 3j Complex numbers


Sequence str "Python" Textual data
list [1, 2, 3] Mutable, ordered collection

tuple (1, 2, 3) Immutable, ordered collection


Set set {1, 2, 3} Unordered, unique items
frozenset frozenset([1, 2, 3]) Immutable set
Mapping dict {"a": 1, "b": 2} Key-value pairs
Boolean bool True, False Logical values
Binary bytes b"Hello" Immutable byte sequences
bytearray bytearray(5) Mutable byte sequences
memoryvie memoryview(b"Hello"
Memory view of binary data
w )

10 of 107
Python Comments
Comments in Python are non-executable lines in the code that help explain what the code does.
They are ignored by the Python interpreter and serve only for documentation and clarity.

🔹 Types of Comments in Python

1. Single-line Comments

• Begin with a # symbol.


• Everything after # on the same line is treated as a comment.

Example:

python
# This is a single-line comment
name = "Alice" # Assigning value to variable

2. Multi-line Comments (Block Comments)

• Python does not have a native multi-line comment syntax like some other languages.
• However, you can use a series of # symbols, or a multi-line string (triple quotes) as a
workaround.

Using multiple #:

python
# This is a multi-line
# comment using multiple
# hash symbols

Using triple quotes (''' or """):

python
"""
This is a multi-line string
used as a comment. Technically,
it's a string not assigned to a variable.
"""

11 of 107
Note: Triple-quoted strings are often used as docstrings when placed at the beginning of
functions, classes, or modules.

🔹 Best Practices for Writing Comments

• Keep comments short, clear, and relevant.


• Update comments when modifying code.
• Avoid obvious comments (e.g., # increment i by 1 for i += 1).
• Use comments to explain why something is done, not just what is done.

Operators in Python
Operators in Python are special symbols or keywords used to perform operations on variables
and values. Python supports a wide variety of operators, categorized by the type of operation
they perform.

🔹 1. Arithmetic Operators

Used to perform mathematical operations.

Operator Description Example Result


+ Addition 5+2 7
- Subtraction 5-2 3
* Multiplication 5*2 10
/ Division 5/2 2.5
// Floor Division 5 // 2 2
% Modulus 5%2 1
** Exponentiation 5 ** 2 25

🔹 2. Comparison (Relational) Operators

Operator Description Example Result


== Equal to 5 == 5 True
!= Not equal to 5 != 3 True
> Greater than 5>2 True
< Less than 3<5 True
>= Greater than or equal 5 >= 5 True
<= Less than or equal 3 <= 4 True

12 of 107
Used to compare the values

🔹 3. Logical Operators

Used to combine conditional statements.

Operator Description Example Result


and Logical AND True and False False
or Logical OR True or False True
not Logical NOT not True False

🔹 4. Assignment Operators

Used to assign values to variables.

Operator Description Example


= Assign x=5
+= Add and assign x += 3
-= Subtract and assign x -= 2
*= Multiply and assign x *= 4
/= Divide and assign x /= 2
//= Floor divide and assign x //= 2
%= Modulus and assign x %= 3
**= Exponent and assign x **= 2

🔹 5. Bitwise Operators

Operate on binary representations of integers.

Operator Description Example Result


& AND 5&3 1
` ` OR `5
^ XOR 5^3 6
~ NOT ~5 -6
<< Left shift 5 << 1 10

13 of 107
>> Right shift 5 >> 1 2

🔹 6. Identity Operators

Used to compare memory locations of two objects.

Operator Description Example Result


is Same object? x is y True/False
is not Different objects? x is not y True/False

🔹 7. Membership Operators

Used to test if a value is a member of a sequence.

Operator Description Example Result


in Present in sequence? "a" in "cat" True
True
not in Not present? "x" not in "dog"

Python Keywords
Keywords are reserved words in Python that have special meaning and cannot be used as
identifiers (like variable names, function names, etc.). They form the syntax and structure of
Python programs.

Python keywords are case-sensitive and are always written in lowercase, except for True,
False, and None.

🔹 Purpose of Keywords

• Define control flow (if, else, while, for)


• Define functions and classes (def, class)
• Handle exceptions (try, except, raise)
• Manage scope and logic (global, nonlocal, in, is)
• Represent constants (True, False, None)

14 of 107
🔹 List of Python Keywords (Python 3.10+)

False None True and as assert


async await break class continue def
del elif else except finally for
from global if import in is
lambda nonlocal not or pass raise
return try while with yield match

Use help("keywords") or keyword.kwlist in the Python interpreter to view the current list of
keywords.

🔹 Examples

python
if True:
print("Condition is True") # 'if' and 'True' are keywords

def greet():
pass # 'def' and 'pass' are keywords

🔹 Best Practices

• Never use keywords as variable or function names.


• Use descriptive names to avoid confusion with keywords.
• Refer to the official documentation for version-specific keyword lists.

Operator Precedence in Python


Operator precedence determines the order in which operations are performed when an
expression has multiple operators. Python follows a well-defined hierarchy of precedence levels
to evaluate expressions correctly.

15 of 107
Understanding precedence is crucial for writing accurate and predictable code, especially in
complex expressions.

🔹 Order of Precedence (Highest to Lowest)

Precedence
Operator(s) Description
Level
1 () Parentheses (expression grouping)
2 ** Exponentiation
3 +x, -x, ~x Unary plus, minus, bitwise NOT
Multiplication, division, floor division,
4 *, /, //, %
modulus
5 +, - Addition and subtraction
6 <<, >> Bitwise shift operators
7 & Bitwise AND
8 ^ Bitwise XOR
9 ` `
==, !=, >, <, >=, <=, is, is not, in, not
10 Comparison and membership operators
in
11 not Logical NOT
12 and Logical AND
13 or Logical OR
14 if – else Conditional expressions
15 lambda Lambda function expression
16 =, +=, -=, etc. Assignment operators

🔹 Example:

python
CopyEdit
result = 3 + 4 * 2 ** 2
# Evaluation order:
# 2 ** 2 = 4
# 4 * 4 = 16
# 3 + 16 = 19

To override precedence, always use parentheses:

16 of 107
python
result = (3 + 4) * 2 ** 2 # (3 + 4) = 7, 7 * 4 = 28

Best Practices

• Use parentheses to make expressions more readable and maintainable.


• When in doubt, rely on parentheses to clarify intent.
• Understand precedence to avoid logic errors in complex conditions or calculations.

Type Conversion in Python


Type conversion refers to changing the data type of a value or variable from one type to another.
In Python, it is broadly categorized into:

1. Implicit Type Conversion


2. Explicit Type Conversion (Type Casting)

🔹 1. Implicit Type Conversion

In implicit conversion, Python automatically converts one data type to another without user
intervention. This usually happens when a smaller data type is promoted to a larger one to avoid
data loss.

Example:

python
x = 10 # Integer
y = 3.5 # Float

result = x + y
print(result) # 13.5
print(type(result)) # <class 'float'>

Here, int is automatically converted to float before addition.

17 of 107
🔹 2. Explicit Type Conversion (Type Casting)

In explicit conversion, the programmer manually converts one data type to another using built-
in functions.

🔸 Common Type Conversion Functions:

Function Description Example Result


int(x) Converts to integer int(3.8) 3
float(x) Converts to float float(5) 5.0
str(x) Converts to string str(25) '25'
bool(x) Converts to boolean bool(0) False
list(x) Converts to list list('abc') ['a', 'b', 'c']
tuple(x) Converts to tuple tuple([1, 2]) (1, 2)
set(x) Converts to set set('aab') {'a', 'b'}

🔹 Example of Explicit Conversion:

python
a = "123"
b = int(a) # Convert string to integer
print(b + 1) # Output: 124

Improper conversion (e.g., int("abc")) raises a ValueError.

Best Practices

• Use type checking with type() before converting.


• Always validate user input before conversion.
• Be cautious with float-to-int conversions (they truncate decimal parts).

Input and Output in Python


Python provides simple and flexible ways to handle input from users and display output to the
screen. These are essential for interacting with programs during execution.

18 of 107
🔹 1. Output in Python

The primary function used to display output is the print() function.

Syntax:

print(object(s), sep=' ', end='\n', file=sys.stdout)

Examples:

print("Hello, World!") # Simple output


print("Name:", "Alice", sep=" -> ") # Custom separator
print("Hello", end=" ") # Custom end character
print("World") # Continues on same line

🔸 Multiple arguments:

python
x = 10
y = 20
print("Sum of", x, "and", y, "is", x + y)

🔹 2. Input in Python

The input() function is used to take user input as a string.

Syntax:

python
variable = input("Prompt message")

Example:

python
name = input("Enter your name: ")
print("Welcome,", name)

📌 Note: input() always returns data as a string, so explicit type conversion may be needed for
numbers.

19 of 107
age = int(input("Enter your age: "))
print("You will be", age + 1, "next year.")

🔹 Advanced Output Formatting

Python supports formatted output using:

🔸 f-Strings (Python 3.6+):

python
name = "Alice"
score = 95.5
print(f"{name} scored {score:.2f} marks.")

🔸 format() method:

python
print("Hello, {}!".format("Bob"))

Best Practices

• Use descriptive prompt messages for input.


• Convert input types as needed (int(), float(), etc.).
• Prefer f-strings for readable and efficient formatting.

Number Systems in Python


Python supports four types of number systems:

Number System Base Digits Used Prefix in Python


Decimal 10 0–9 No prefix
Binary 2 0, 1 0b or 0B
Octal 8 0–7 0o or 0O
Hexadecimal 16 0–9, A–F (or a–f) 0x or 0X

1. Decimal Numbers (Base 10)

• Default number system in Python.


20 of 107
• Uses digits 0 to 9.

python
num = 100
print(num) # 100 (Decimal)

2. Binary Numbers (Base 2)

• Uses only 0 and 1.


• Prefix: 0b or 0B

python
CopyEdit
bin_num = 0b1010
print(bin_num) # 10 (in Decimal)

3. Octal Numbers (Base 8)

• Uses digits from 0 to 7.


• Prefix: 0o or 0O

python
oct_num = 0o12
print(oct_num) # 10 (in Decimal)

4. Hexadecimal Numbers (Base 16)

• Uses 0–9 and A–F (10–15).


• Prefix: 0x or 0X

python
hex_num = 0xA
print(hex_num) # 10 (in Decimal)

Conversion Between Number Systems


Python provides built-in functions:

Conversion Function Example


Decimal → Binary bin(x) bin(10) → '0b1010'

21 of 107
Decimal → Octal oct(x) oct(10) → '0o12'
Decimal → Hexadecimal hex(x) hex(10) → '0xa'
Binary → Decimal int('0b1010', 2) → 10
Octal → Decimal int('0o12', 8) → 10
Hexadecimal → Decimal int('0xa', 16) → 10

Example (All Conversions)


python
x = 10

print(bin(x)) # '0b1010'
print(oct(x)) # '0o12'
print(hex(x)) # '0xa'

# Converting back to decimal


print(int('0b1010', 2)) # 10
print(int('0o12', 8)) # 10
print(int('0xa', 16)) # 10

Summary Table – Conversion in Python

From → To Python Code Example


Decimal → Binary bin(10) → '0b1010'
Decimal → Octal oct(10) → '0o12'
Decimal → Hex hex(10) → '0xa'
Binary → Decimal int('0b1010', 2) → 10
Octal → Decimal int('0o12', 8) → 10
Hex → Decimal int('0xa', 16) → 10

Controll Statements

22 of 107
Control statements in Python are used to direct the flow of execution in a program. They allow
the program to make decisions, repeat operations, and jump between different parts of code
based on specific conditions.

These statements control the order in which instructions are executed, enabling dynamic,
flexible, and intelligent behavior in Python programs.

1. Conditional Statements (Decision-Making)

Used to execute certain blocks of code based on whether a condition is True or


False.

• if Statement
• if-else Statement
• if-elif-else Ladder
• Nested if

23 of 107
Example:

python
x = 10
if x > 0:
print("Positive number")
elif x == 0:
print("Zero")
else:
print("Negative number")

2. Looping Statements (Iteration)

Used to execute a block of code repeatedly as long as a specified condition is


true.

• for Loop – Iterates over a sequence (list, tuple, string, etc.)


• while Loop – Repeats as long as the condition remains true

Example (for loop):

python
for i in range(5):
print(i)

Example (while loop):

python
count = 0
while count < 5:
print(count)
count += 1

24 of 107
3. Jumping Statements (Flow Control Within Loops)

Used to alter the normal flow of loops and other code blocks.

• break – Terminates the loop prematurely


• continue – Skips the current iteration and moves to the next
• pass – A null operation; does nothing (used as a placeholder)

Example:

for i in range(5):
if i == 3:
break
print(i)

Summary Table
Type Statements Purpose
Conditional if, if-else, if-elif-else Make decisions based on conditions
Looping for, while Repeat a block of code
Jumping break, continue, pass Control loop execution flow

Strings
What is a String in Python?

A string in Python is a sequence of Unicode characters enclosed in either single


quotes ('), double quotes ("), or triple quotes (''' or """). Strings are
immutable, meaning their values cannot be changed after creation.

Examples:

python
str1 = 'Hello'
str2 = "World"
str3 = '''Python is fun'''

25 of 107
Creating Strings

# Single-line string
name = "Alice"

# Multi-line string using triple quotes


description = """Python is
a powerful programming language."""

Accessing Characters in a String


Ex:
text = "Python"
print(text[0]) # Output: P
print(text[-1]) # Output: n

String Slicing
Python code:
text = "Programming"
print(text[0:6]) # Output: Progra
print(text[3:]) # Output: gramming
print(text[:4]) # Output: Prog

Common String Methods

Method Description Example


lower() Converts all characters to lowercase "HELLO".lower() → 'hello'
upper() Converts all characters to uppercase "hello".upper() → 'HELLO'
"hello world".title() →
title() Capitalizes the first letter of each word
'Hello World'
"python".capitalize() →
capitalize() Capitalizes the first character only
'Python'
strip() Removes leading and trailing whitespace " hello ".strip() → 'hello'

26 of 107
replace(old, "java".replace("j", "p") →
Replaces a substring with another
new) 'pava'
find(sub) Returns the index of first occurrence "code".find("d") → 2
count(sub) Counts occurrences of a substring "banana".count("a") → 3
startswith(s Checks if the string starts with a specific "hello".startswith("he") →
ub) substring True
endswith(sub Checks if the string ends with a specific "python".endswith("on") →
) substring True
split(delimi "a,b,c".split(",") → ['a',
Splits a string into a list
ter) 'b', 'c']
join(list) Joins elements of a list into a string ",".join(['a', 'b']) → 'a,b'

String Formatting in Python

1. Using f-strings (Python 3.6+)

Python code:
name = "Alice"
age = 25
print(f"My name is {name} and I am {age} years old.")

2. Using format() method

python
print("My name is {} and I am {} years old.".format("Alice",
25))

Immutability of Strings
python
text = "hello"
# text[0] = "H" # Error! Strings are immutable
text = "Hello" # Reassigning is allowed

27 of 107
📚 Lists in Python – Complete Concept

🔷 1. Definition

A List in Python is an ordered, mutable, and indexed collection used to store multiple
items in a single variable. Lists allow heterogeneous elements and duplicate values.

A List is a built-in data type in Python used to store multiple values in a single variable.
It is:

• Ordered (maintains insertion order)


• Mutable (can be modified)
• Indexed (supports indexing & slicing)
• Heterogeneous (can store any data type)
• Allows duplicates

0 1 2 3 4

List= [ 10 20 30 40 50 ]
-5 -4 -3 -2 -1

🔹 2. Creating a List
Python code
# Empty list
a = []
# List of numbers
nums = [1, 2, 3, 4]
# Mixed data types
info = ["Nasir", 21, True]
# Nested list
nested = [1, [2, 3], 4]

28 of 107
🔹 3. Accessing Elements

my_list = ['a', 'b', 'c', 'd']

print(my_list[0]) # 'a'
print(my_list[-1]) # 'd' (last)
print(my_list[1:3]) # ['b', 'c'] (slicing)

🔹 4. Modifying Elements

my_list[1] = 'z' # Replace 'b' with 'z'

🔹 5. Deleting Elements

python
del my_list[2] # Delete by index
my_list.remove('z') # Delete by value
popped = my_list.pop(0) # Remove and return item
my_list.clear() # Remove all items

🔹 6. Useful List Methods

Method Description
append(x) Add item to end
insert(i, x) Insert at index
extend(iter) Add multiple elements
remove(x) Remove first matching value
pop(i) Remove by index and return it
clear() Remove all elements
index(x) Return first index of value
count(x) Count how many times a value appears
sort() Sort in ascending order
reverse() Reverse list in-place
copy() Return a shallow copy

29 of 107
Python code:
nums = [3, 1, 4]
nums.append(2)
nums.sort()
print(nums) # [1, 2, 3, 4]

🔹 7. List Operations

Operation Syntax Example Result


Concatenation [1, 2] + [3, 4] [1, 2, 3, 4]
Repetition [0] * 3 [0, 0, 0]
Membership 3 in [1, 2, 3] True
Length len([1, 2, 3]) 3

🔹 8. Iterating Over a List

Python code:
for item in ["a", "b", "c"]:
print(item)

🔹 9. List Comprehension (Advanced)

Efficient way to generate lists.

Python code
squares = [x**2 for x in range(5)]
# [0, 1, 4, 9, 16]

With condition:

Python code
even = [x for x in range(10) if x % 2 == 0]
# [0, 2, 4, 6, 8]

🔹 10. Nested Lists (2D Lists)

Python code

30 of 107
matrix = [
[1, 2, 3],
[4, 5, 6]
]
print(matrix[1][2]) # 6

🔹 11. Interview Notes

• Lists are mutable.


• Use .copy() to avoid reference issues.
• Commonly used in real-world data (e.g., JSON, arrays).
• Lists are implemented as dynamic arrays in CPython.

🔹 12. Summary Table

Feature Value
Ordered Yes
Indexed Yes
Mutable Yes
Allows Duplicates Yes
Heterogeneous Yes
Nesting Allowed Yes

Python List Methods – Complete Guide

🔹 1. append(x)

Description: Adds an element x to the end of the list.

python

31 of 107
fruits = ["apple", "banana"]
fruits.append("cherry")
print(fruits) # ['apple', 'banana', 'cherry']

🔹 2. insert(i, x)

Description: Inserts an element x at a specified index i. All elements after index i shift
right.

python
fruits = ["apple", "banana"]
fruits.insert(1, "cherry")
print(fruits) # ['apple', 'cherry', 'banana']

🔹 3. extend(iter)

Description: Extends the list by appending elements from an iterable (e.g., list, tuple).

Python code
fruits = ["apple", "banana"]
fruits.extend(["cherry", "date"])
print(fruits) # ['apple', 'banana', 'cherry', 'date']

🔹 4. remove(x)

Description: Removes the first occurrence of x from the list. Raises a ValueError if x is
not in the list.

python
fruits = ["apple", "banana", "cherry"]
fruits.remove("banana")
print(fruits) # ['apple', 'cherry']

🔹 5. pop(i)

Description: Removes and returns the item at the given index i. If no index is provided,
removes and returns the last item. Raises an IndexError if the list is empty.

python

32 of 107
fruits = ["apple", "banana", "cherry"]
item = fruits.pop(1)
print(item) # 'banana'
print(fruits) # ['apple', 'cherry']

🔹 6. clear()

Description: Removes all elements from the list.

python
fruits = ["apple", "banana", "cherry"]
fruits.clear()
print(fruits) # []

🔹 7. index(x)

Description: Returns the index of the first occurrence of x in the list. Raises a ValueError
if x is not in the list.

python
fruits = ["apple", "banana", "cherry"]
index = fruits.index("banana")
print(index) # 1

🔹 8. count(x)

Description: Returns the number of occurrences of x in the list.

python
fruits = ["apple", "banana", "cherry", "apple"]
count = fruits.count("apple")
print(count) # 2

🔹 9. sort()

Description: Sorts the list in ascending order (in-place). To sort in descending order, use
reverse=True.

33 of 107
python
numbers = [4, 2, 8, 6]
numbers.sort()
print(numbers) # [2, 4, 6, 8]

# Sorting in reverse order


numbers.sort(reverse=True)
print(numbers) # [8, 6, 4, 2]

🔹 10. reverse()

Description: Reverses the order of elements in the list (in-place).

python
fruits = ["apple", "banana", "cherry"]
fruits.reverse()
print(fruits) # ['cherry', 'banana', 'apple']

🔹 11. copy()

Description: Returns a shallow copy of the list.

python
fruits = ["apple", "banana", "cherry"]
new_fruits = fruits.copy()
print(new_fruits) # ['apple', 'banana', 'cherry']

🔹 12. join() (Used with strings)

Description: While not a direct method on lists, you can join a list of strings into a single
string using the join() method from the str class.

python
words = ["Hello", "World"]
sentence = " ".join(words)
print(sentence) # 'Hello World'

34 of 107
🔹 13. list() Constructor

Description: Converts an iterable (e.g., tuple, set, string) to a list.

python
CopyEdit
tuple_data = (1, 2, 3)
list_data = list(tuple_data)
print(list_data) # [1, 2, 3]

🔹 14. copy.deepcopy() (from copy module)

Description: Performs a deep copy of a list, where nested objects are copied as well.

python
import copy
original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)
deep_copy[0][0] = 9
print(original) # [[1, 2], [3, 4]]
print(deep_copy) # [[9, 2], [3, 4]]

🔹 15. list comprehension (Advanced)

Description: A concise way to create lists based on existing lists, with optional conditions.

python
CopyEdit
# Basic example
squares = [x**2 for x in range(5)]
print(squares) # [0, 1, 4, 9, 16]

# With condition
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]

35 of 107
🔹 16. Summary Table of List Methods

Method Description Example


append(x) Add an element to the end list.append(5)
insert(i, list.insert(2,
Insert an element at index i
x) "banana")
extend(ite
Extend list with elements from iter list.extend([1, 2, 3])
r)
remove(x) Remove the first occurrence of x list.remove("apple")
pop(i) Remove and return item at index i list.pop(0)
clear() Remove all elements list.clear()
index(x) Return index of first occurrence of x list.index("banana")
count(x) Count occurrences of x list.count("apple")
sort() Sort list in ascending order list.sort()
reverse() Reverse the order of the list list.reverse()
copy() Return a shallow copy of the list list.copy()
list() Convert iterable to list list((1, 2, 3))
(from copy module) Deep copy of the
deepcopy() copy.deepcopy(list)
list
" ".join(["hello",
join() Join a list of strings into a single string
"world"])

Example Use Case: Combining Methods


python
fruits = ["apple", "banana", "cherry"]
fruits.append("date")
fruits.insert(1, "orange")
fruits.remove("banana")
fruits.sort()
# Display modified list
print(fruits) # ['apple', 'cherry', 'date', 'orange']

36 of 107
Python List Operations

🔹 1. Indexing

Description: Lists are ordered, meaning each element has an index. You can access
elements by using indices.

• Positive Indexing: Starts from 0 for the first element.


• Negative Indexing: Starts from -1 for the last element.

Python code
fruits = ["apple", "banana", "cherry"]
print(fruits[1]) # 'banana'
print(fruits[-1]) # 'cherry'

🔹 2. Slicing

Description: A subset of a list can be accessed by specifying a range of indices. Syntax:


list[start:stop]

• start: Index of the first element.


• stop: Index of the element to stop at (not included).

python
fruits = ["apple", "banana", "cherry", "date"]
print(fruits[1:3]) # ['banana', 'cherry']
print(fruits[:2]) # ['apple', 'banana']
print(fruits[2:]) # ['cherry', 'date']

🔹 3. Concatenation (+)

Description: Combine two lists using the + operator to form a new list.

python
list1 = [1, 2, 3]
list2 = [4, 5, 6]
result = list1 + list2
print(result) # [1, 2, 3, 4, 5, 6]

37 of 107
🔹 4. Repetition (*)

Description: Repeat a list multiple times using the * operator.

python
fruits = ["apple", "banana"]
print(fruits * 2) # ['apple', 'banana', 'apple', 'banana']

🔹 5. Membership (in, not in)

Description: Check if an element exists in a list or not.

python
fruits = ["apple", "banana", "cherry"]
print("banana" in fruits) # True
print("date" not in fruits) # True

🔹 6. Length (len())

Description: Return the number of items in the list.

python
fruits = ["apple", "banana", "cherry"]
print(len(fruits)) # 3

🔹 7. Min/Max (min(), max())

Description: Get the smallest and largest element in a list (works with numeric lists).

python
numbers = [1, 2, 3, 4, 5]
print(min(numbers)) # 1
print(max(numbers)) # 5

🔹 8. Sum (sum())

Description: Return the sum of elements in a list (works with numeric lists).

38 of 107
python
numbers = [1, 2, 3, 4]
print(sum(numbers)) # 10

🔹 9. Sorting (sort(), sorted())

Description: Sorts the list in-place or returns a new sorted list.

• sort(): Sorts the list in-place.


• sorted(): Returns a new sorted list.

python
numbers = [5, 2, 9, 1]
numbers.sort()
print(numbers) # [1, 2, 5, 9]

# sorted() doesn't change the original list


new_numbers = sorted(numbers, reverse=True)
print(new_numbers) # [9, 5, 2, 1]

🔹 10. Reversing (reverse())

Description: Reverses the elements of the list in-place.

python
fruits = ["apple", "banana", "cherry"]
fruits.reverse()
print(fruits) # ['cherry', 'banana', 'apple']

🔹 11. Copying Lists (copy(), list())

Description:

• Use copy() to get a shallow copy of the list.


• Use list() constructor to create a new list from an iterable.

python
fruits = ["apple", "banana", "cherry"]
new_fruits = fruits.copy() # or new_fruits = list(fruits)

39 of 107
new_fruits.append("date")
print(fruits) # ['apple', 'banana', 'cherry']
print(new_fruits) # ['apple', 'banana', 'cherry', 'date']

🔹 12. List Comprehension

Description: A concise way to create a new list by performing an operation on each


element of an existing list, optionally filtering elements based on a condition.

python
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers]
print(squares) # [1, 4, 9, 16, 25]

# Filtering with condition


even_squares = [x**2 for x in numbers if x % 2 == 0]
print(even_squares) # [4, 16]

🔹 13. Zipping Lists (zip())

Description: Combine two or more lists element-wise into tuples.

python
names = ["John", "Jane", "Jack"]
ages = [25, 30, 35]
zipped = zip(names, ages)
print(list(zipped)) # [('John', 25), ('Jane', 30), ('Jack', 35)]

🔹 14. Nested List Operations

Description: Lists can contain other lists as elements (nested lists). You can perform
operations on nested lists like accessing, modifying, and iterating through them.

python
nested_list = [[1, 2], [3, 4], [5, 6]]
print(nested_list[1][0]) # 3
nested_list[0][1] = 8
print(nested_list) # [[1, 8], [3, 4], [5, 6]]
40 of 107
🔹 15. Example Use Case: List Operations

python
# List with initial elements
fruits = ["apple", "banana", "cherry"]
# Add elements
fruits.append("date")
fruits.insert(1, "orange")
# Remove an element
fruits.remove("banana")

# Reverse the list


fruits.reverse()

# Concatenate and repeat


more_fruits = ["fig", "grape"]
all_fruits = fruits + more_fruits * 2

# Final result
print(all_fruits) # ['date', 'cherry', 'orange', 'apple', 'fig',
'grape', 'fig', 'grape']

Summary Table of List Operations

Operation Description Example


Indexing Access elements by index fruits[1]
Extract a sublist using a range of
Slicing fruits[1:3]
indices
Concatenation (+) Combine lists list1 + list2
Repetition (*) Repeat a list multiple times list * 3
Check if an element exists in the
Membership (in) "banana" in fruits
list
Length (len()) Get the number of items in the list len(fruits)
Min/Max (min()) Find smallest/largest element min(list), max(list)
Sum all elements of the list
Sum (sum()) sum(list)
(numeric)

41 of 107
Sorting (sort()) Sort list in ascending order fruits.sort()
Reversing
Reverse the order of the list fruits.reverse()
(reverse())
new_fruits =
Copying (copy()) Get a shallow copy of the list
fruits.copy()
Create a new list from an existing
Comprehension [x*2 for x in list]
list
Zipping Combine multiple lists into tuples zip(list1, list2)
Lists inside lists (multidimensional
Nested Lists nested[1][0]
lists)

Tuples in Python

What is a Tuple?

A tuple is an ordered, immutable collection of items in Python. It can store


heterogeneous data types (integers, strings, lists, etc.), and items are enclosed in
parentheses ().

Key Characteristics of Tuples:

• Ordered: Items maintain their insertion order.


• Immutable: Once defined, tuple elements cannot be changed or modified.
• Allows Duplicates: Tuples can contain duplicate values.
• Heterogeneous: Can hold elements of different data types.

Ex:

t1 = (1, 2, 3)
t2 = ("apple", "banana", "cherry")
t3 = (1, "hello", 3.5)
t4 = () # Empty tuple
t5 = (5,) # Single-element tuple (comma is necessary)

42 of 107
Accessing Tuple Elements:

my_tuple = ("a", "b", "c", "d")


print(my_tuple[0]) # Output: 'a'
print(my_tuple[-1]) # Output: 'd'

Tuple Operations:
Operation Description Example
Accessing specific
Indexing t[1] → Returns second element
elements
Extracting a range of t[1:3] → Returns elements at index 1
Slicing
elements and 2
Concatena
Combining two tuples t1 + t2
tion
Repetition Repeating tuple elements t * 3
Membershi Checking if an element
'apple' in t
p exists
Iteration Looping through elements for i in t: print(i)

Built-in Tuple Functions/Methods:

Function /
Description Example
Method
len(tuple) Returns number of items in the tuple len(t)
Returns the maximum value (numeric or max((4, 1, 9)) →
max(tuple)
alphabetic) 9
min((4, 1, 9)) →
min(tuple) Returns the minimum value
1
sum((1, 2, 3)) →
sum(tuple) Returns the sum of all numeric elements
6
tuple([1, 2]) →
tuple(seq) Converts a sequence (like list) into a tuple
(1, 2)
count(x) Returns the number of times x appears t.count(2)
index(x) Returns the index of the first occurrence of x t.index("apple")

43 of 107
Immutability in Practice:
t = (10, 20, 30)
# t[1] = 50 # Error: Tuples are immutable

You can, however, create a new tuple from an existing one:

t = (10, 20, 30)


t = t + (40,) # Creates a new tuple

Tuple Packing and Unpacking

Packing:

t = 1, "hello", 3.14 # Automatically packed into a tuple

Unpacking:

a, b, c = t
print(a) # 1
print(b) # hello

Use Cases of Tuples:

• Representing fixed collections (e.g., coordinates, RGB values).


• Dictionary keys (only immutable data types like tuples can be used as keys).
• Return multiple values from functions.

Dictionaries in Python

What is a Dictionary?

A dictionary in Python is an unordered, mutable, and indexed collection of key-value


pairs. It is one of Python’s most powerful built-in data types, used for storing and
accessing data efficiently.

• Keys must be unique and immutable (strings, numbers, tuples).


• Values can be of any data type and may be duplicated.

44 of 107
Creating a Dictionary:

# Using curly braces


student = {"name": "Alice", "age": 22, "course": "MCA"}
# Using the dict() constructor
info = dict(id=101, branch="CSE")

Accessing Dictionary Elements:


print(student["name"]) # Output: Alice
print(student.get("age")) # Output: 22

• get() method is preferred as it returns None if the key is not found instead of
raising an error.

Modifying and Adding Items:

student["age"] = 23 # Modifies value


student["university"] = "VEMU" # Adds new key-value pair

Deleting Elements:

del student["course"] # Deletes specific key


student.pop("age") # Removes and returns value
student.clear() # Removes all items

Dictionary Methods:

Method Description Example


keys() Returns a view of all keys student.keys()
values() Returns a view of all values student.values()
items() Returns a view of key-value pairs as tuples student.items()
get(key) Returns value for the key, or None if missing student.get("name")
update(d student.update({"grade":
Merges another dictionary into current
ict2) "A"})
pop(key) Removes and returns value of specified key student.pop("age")
clear() Removes all key-value pairs student.clear()

45 of 107
new_dict =
copy() Returns a shallow copy of the dictionary
student.copy()
setdefau Returns value if key exists, else inserts key with student.setdefault("grad
lt() default e", "B")

Iterating Through a Dictionary:

for key in student:


print(key, ":", student[key])

# OR using items()
for key, value in student.items():
print(key, "=>", value)

Dictionary Comprehension:

A concise way to create dictionaries from iterables.

squares = {x: x**2 for x in range(5)}


# Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Nested Dictionaries:

Dictionaries can contain other dictionaries as values.

students = {
"S1": {"name": "Alice", "age": 21},
"S2": {"name": "Bob", "age": 22}
}
Accessing nested data:

print(students["S1"]["name"]) # Output: Alice

Conclusion

Dictionaries in Python are ideal for storing structured, key-based data efficiently. Their
fast access time, dynamic nature, and rich set of built-in methods make them

46 of 107
indispensable for data manipulation, configuration management, and backend logic in full
stack development.

Set

A set is an unordered, unindexed, and mutable collection of unique elements in Python.


Sets are commonly used for membership testing, removing duplicates, and performing
mathematical set operations like union, intersection, and difference.

• Duplicates are automatically removed.


• Set elements must be immutable, but the set itself is mutable.

Creating Sets:

# Using curly braces


my_set = {1, 2, 3, 4}

# Using the set() constructor


empty_set = set() # Correct way to create an empty set
mixed_set = set([1, "hello", 3.5])

Accessing Elements:

• Sets do not support indexing or slicing because they are unordered.


• You can loop through a set:

Ex:
for item in my_set:
print(item)

Modifying Sets:

my_set.add(5) # Adds a single element


my_set.update([6, 7, 8]) # Adds multiple elements (iterable)

Removing Elements:
my_set.remove(3) # Removes element; raises KeyError if
not found
47 of 107
my_set.discard(10) # Removes element; does nothing if not
found
my_set.pop() # Removes and returns a random item
my_set.clear() # Removes all elements from the set

Set Operations:

Operation Description Syntax/Example


Combines all unique elements from two
Union A.union(B) or `A
sets
Intersection Common elements in both sets A.intersection(B) or A & B
Difference Elements in A but not in B A.difference(B) or A - B
Symmetric A.symmetric_difference(B)
Elements in A or B but not in both
Difference or A ^ B
Subset Checks if A is subset of B A.issubset(B)
Superset Checks if A is superset of B A.issuperset(B)
Checks if A and B have no common
Disjoint A.isdisjoint(B)
elements

Set Example:

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

print(A | B) # Union: {1, 2, 3, 4, 5, 6}


print(A & B) # Intersection: {3, 4}
print(A - B) # Difference: {1, 2}
print(A ^ B) # Symmetric Difference: {1, 2, 5, 6}

Frozen Sets

A frozenset is an immutable version of a set. Once created, it cannot be modified (no add
or remove).

fs = frozenset([1, 2, 3])

48 of 107
Useful when you need a set to be used as a dictionary key or need it to remain unchanged.

Conclusion

Sets are a powerful data structure in Python for handling unique elements and
performing mathematical operations. Their high-performance membership testing and
flexible methods make them essential for data cleaning, analytics, and backend logic in
full stack development.

Functions in Python

What is a Function?

A function in Python is a block of reusable code that performs a specific task. Functions
help in organizing code into modular, readable, and maintainable components.

Advantages of Using Functions:

• Promotes code reuse


• Increases readability and modularity
• Makes debugging and testing easier
• Helps in logical organization of code

Defining a Function in Python:

def function_name(parameters):
# function body
return value # optional

Example:

def greet(name):
return f"Hello, {name}!"

print(greet("Alice")) # Output: Hello, Alice!

49 of 107
Function Arguments in Python:
Type Description Example
Positional Arguments Passed in correct position/order greet("John")
greet(name="John"
Keyword Arguments Passed with key=value format
)
def
Default Arguments Provide default values for parameters greet(name="Guest
")
Variable-length Allows passing multiple values as tuple
*args, **kwargs
Arguments or dict

Example of all:

def profile(name, age=20, *skills, **details):


print(name, age, skills, details)

profile("Alice", 25, "Python", "Django", city="Chennai", batch="2025")

Types of Functions in Python:

Type of Function Description Example


Built-in Functions Predefined by Python len(), print(), sum()
User-defined
Created by the user def greet():
Functions
Lambda Functions Anonymous one-liner functions lambda x: x * 2
Recursive def factorial(n): return n
Function that calls itself
Functions * factorial(n-1)
Higher-order Accept other functions as arguments or
map(), filter(), reduce()
Functions return them

Lambda (Anonymous) Function:

Used for short, throwaway functions.

50 of 107
square = lambda x: x ** 2
print(square(5)) # Output: 25
Return Statement:

• Used to send results from a function back to the caller.

def add(a, b):


return a + b

If no return is provided, the function returns None by default.

Function Scope:

• Local Scope: Variables declared inside the function.


• Global Scope: Variables declared outside and accessible inside functions using
global keyword.

Conclusion

Functions are fundamental to writing clean, reusable, and efficient code in Python. By
mastering function definitions, arguments, and types, developers can build scalable and
maintainable applications in full stack development.

Lambda Functions in Python

What is a Lambda Function?

A lambda function is a small, anonymous (unnamed) function defined using the lambda
keyword in Python. It can take any number of arguments but can have only one
expression.

Syntax:

lambda arguments: expression

Example:
square = lambda x: x * x
print(square(5)) # Output: 25

51 of 107
Use Cases of Lambda Functions:

Lambda functions are commonly used when:

• A function is needed for a short period of time


• Used as an argument to higher-order functions like map(), filter(), reduce()

Example with map():

nums = [1, 2, 3, 4]
squares = list(map(lambda x: x ** 2, nums))
print(squares) # Output: [1, 4, 9, 16]

Key Points:

• Lambda functions do not use the def keyword


• They return the result of the expression automatically
• Best for short, simple logic
• Cannot contain multiple expressions or statements

Conclusion:

Lambda functions offer a concise way to create throwaway or inline functions. They are
especially useful for functional programming patterns and scenarios where a full function
definition would be unnecessary.

Recursion

Recursion is a programming technique where a function calls itself to solve a problem.


Each recursive call works on a smaller subproblem until it reaches a base case that stops
further calls.

Structure of a Recursive Function:

A recursive function must have:

1. Base Case – defines when the recursion should stop.


2. Recursive Case – where the function calls itself.

52 of 107
Example: Factorial using Recursion

def factorial(n):
if n == 0:
return 1 # Base case
else:
return n * factorial(n - 1) # Recursive case

print(factorial(5)) # Output: 120

Key Points:

• Recursion breaks complex problems into simpler sub-problems.


• Each recursive call adds a new frame to the call stack.
• Improper base cases can lead to infinite recursion and RecursionError.

When to Use Recursion:

• Solving problems that have a recursive nature, such as:


o Factorial
o Fibonacci sequence
o Tree traversal
o Backtracking algorithms

Conclusion:

Recursion is a powerful concept for solving repetitive, nested, or tree-based problems in


a clean and elegant way. However, it must be used with care to avoid performance issues
or stack overflow errors.

Higher-Order Functions in Python

A Higher-Order Function is a function that can do one or both of the following:

53 of 107
1. Accept another function as an argument
2. Return a function as a result

This allows functions to be more flexible, reusable, and enables functional


programming techniques.

Common Higher-Order Functions in Python:

Functi
Description Example
on
map(lambda x: x*2, [1, 2,
map() Applies a function to all items in an iterable
3])
filte filter(lambda x: x > 2,
Filters items based on a condition (function)
r() [1, 2, 3])
reduc Applies a rolling computation to items (from reduce(lambda x, y: x+y,
e() functools) [1, 2, 3])

Example: Using map() with Lambda

nums = [1, 2, 3]
doubled = list(map(lambda x: x * 2, nums))
print(doubled) # Output: [2, 4, 6]

Returning Functions (Closure Example):


def outer_function(msg):
def inner_function():
print("Message:", msg)
return inner_function

greet = outer_function("Hello")
greet() # Output: Message: Hello

Conclusion:

Higher-order functions promote cleaner, modular, and functional-style programming.


They are key tools in building scalable and reusable logic in full stack development using
Python.

54 of 107
What is a Module in Python?

A module is a file containing Python code (functions, classes, variables) that can be
imported and reused in other Python programs. Modules help in organizing code into
manageable, reusable, and logical units.

• Any Python file with a .py extension is considered a module.


• Modules promote code reusability and separation of concerns.

Types of Modules:

Type Description Example


math, os, random,
Built-in Modules Pre-installed with Python
sys
User-defined
Created by the user as .py files mymodule.py
Modules
External libraries installed via numpy, requests,
Third-party Modules
pip flask

Importing Modules:

import math
print(math.sqrt(16)) # Output: 4.0

# Import specific functions


from math import pi, pow
print(pi) # Output: 3.141592653589793

# Import with alias


import datetime as dt
print(dt.datetime.now())

Creating a User-defined Module:

Create a file called mymodule.py:

# mymodule.py
def greet(name):

55 of 107
return f"Hello, {name}!"

Use it in another script:

import mymodule
print(mymodule.greet("Alice")) # Output: Hello, Alice!

Using the dir() Function:

import math
print(dir(math)) # Lists all functions and variables in the module

Built-in Modules Examples:

Module Purpose
math Mathematical functions
random Random number generation
datetim
Date and time manipulation
e
os Interacting with the operating system
System-specific parameters and
sys
functions

Third-party Modules:

Installed using pip (Python package manager):

pip install requests

Example usage:

import requests
response = requests.get("https://api.github.com")
print(response.status_code)

56 of 107
Conclusion:

Modules are essential for organizing, reusing, and scaling code in Python applications.
Whether using built-in libraries or third-party tools, modules enable efficient and
structured full stack development.

Exception

An exception is an error that occurs during the execution of a program, disrupting its
normal flow. Python provides a way to gracefully handle exceptions using built-in
exception handling mechanisms to prevent program crashes.

Common Exceptions:

Exception Type Description


ZeroDivisionError Division by zero
ValueError Invalid value
TypeError Invalid operation between data types
IndexError Index out of range
KeyError Accessing non-existing dictionary key
FileNotFoundError File does not exist

Basic Syntax of Exception Handling:

ptry:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
# Code that runs if an exception occurs
print("You cannot divide by zero!")
finally:
# Code that always runs
print("Execution completed.")

Keywords Used in Exception Handling:

Keyword Purpose

57 of 107
Wraps the code block that might raise an
try
exception
except Handles the exception if it occurs
else Runs if no exceptions occur
finally Executes code regardless of exception
raise Manually raise a specific exception

Example with Multiple Except Blocks:

try:
num = int(input("Enter a number: "))
print(10 / num)
except ValueError:
print("Please enter a valid number.")
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print("No errors occurred.")
finally:
print("Program ended.")

Raising Custom Exceptions:


def check_age(age):
if age < 18:
raise ValueError("Age must be 18 or older.")
return "Access granted"
print(check_age(20))
Conclusion:

Exception handling is essential for writing robust and user-friendly applications. It


ensures that your Python code can deal with unexpected issues gracefully without
crashing, which is especially important in full stack and production-grade applications.

58 of 107
Object-Oriented Programming (OOP) in Python

Object-Oriented Programming (OOP) is a programming paradigm that organizes code using


objects and classes. It promotes modularity, reusability, and a clear structure by
encapsulating data and behavior into single units called objects. Python fully supports
OOP and allows developers to define classes that serve as blueprints for creating objects.

Key principles of OOP in Python include:

• Class and Object: A class defines the structure and behavior, while an object is an
instance of a class.
• Encapsulation: Bundling data and methods within a class to restrict direct access
to internal variables.
• Inheritance: Allows one class to inherit attributes and methods from another,
promoting code reuse.
• Polymorphism: Enables methods to behave differently based on the object calling
them.
• Abstraction: Hides complex implementation details and exposes only the
necessary parts of an object.

Python's OOP features make it suitable for building scalable and maintainable
applications, especially in full-stack development.

Classes in Python
A class in Python is a user-defined blueprint or prototype for creating objects. Classes
encapsulate data (attributes) and functions (methods) that operate on that data. They allow code
to be organized in a clean, modular way, promoting reusability and scalability.

Syntax of a Class

class ClassName:
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2

def method_name(self):
# perform some operation

59 of 107
pass

Key Components

1. class Keyword
Used to define a class.

class Person:
pass

2. Constructor: __init__() Method


This is a special method automatically called when an object is created. It initializes the
object's attributes.

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

3. Attributes
These are variables that belong to the object and hold data about the object.

p = Person("Nasir", 24)
print(p.name) # Output: Nasir

4. Methods
Functions defined inside a class that perform operations using the object’s data.

class Person:
def greet(self):
return f"Hello, my name is {self.name}"

5. self Keyword
Refers to the current instance of the class. It is used to access attributes and methods
within the class.

60 of 107
Example

class Car:
def __init__(self, brand, model):
self.brand = brand
self.model = model

def full_name(self):
return f"{self.brand} {self.model}"

my_car = Car("Toyota", "Camry")


print(my_car.full_name()) # Output: Toyota Camry

Benefits of Using Classes

• Modularity: Code is grouped into logical units.


• Reusability: Once a class is written, it can be used to create multiple objects.
• Encapsulation: Keeps related data and behavior together.
• Maintainability: Changes in logic can be made inside the class without affecting the rest
of the code.

Objects in Python
An object is a concrete instance of a class. It holds actual data and can invoke methods defined
in the class. While the class is just a blueprint, an object is the real-world implementation of that
blueprint.

What is an Object?

In simple terms, if a class is a template, then an object is a real product created using that
template. Each object contains its own data and can perform actions using the methods defined in
its class.

Creating an Object

You create an object by calling the class as if it were a function. This triggers the class’s
__init__() constructor method.

61 of 107
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

# Creating an object
p1 = Person("Nasir", 24)

Here, p1 is an object (or instance) of the Person class.

Accessing Attributes and Methods

Once an object is created, you can access its attributes and methods using dot (.) notation.

print(p1.name) # Output: Nasir


print(p1.age) # Output: 24

Let’s add a method to the class:

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def greet(self):
return f"Hello, I’m {self.name} and I’m {self.age} years old."

p1 = Person("Nasir", 24)
print(p1.greet()) # Output: Hello, I’m Nasir and I’m 24 years
old.

Multiple Objects from the Same Class

You can create as many objects as you want from the same class. Each object will have its own
separate data.

python
CopyEdit

62 of 107
p2 = Person("Ayesha", 22)
print(p2.greet()) # Output: Hello, I’m Ayesha and I’m 22 years
old.

Characteristics of Objects

• Identity: Every object has a unique memory address.


• State: Defined by the values of its attributes.
• Behavior: Defined by the methods it can perform.

Summary

• Objects are the actual entities that interact with your code.
• They are created from classes and can store data and call methods.
• They bring the structure of a class to life by providing real data and behavior.

Methods in Python
A method is a function defined inside a class that operates on the data (attributes) of the class
and performs specific actions. Methods are used to define the behaviors of an object.

While functions are independent blocks of reusable code, methods are tied to objects—they
require an object to be called and usually work with that object's internal state.

Defining a Method

You define a method just like a normal function, but inside a class, and it must take self as its
first parameter.

class Person:
def __init__(self, name):
self.name = name

def greet(self):
return f"Hello, my name is {self.name}"

Here, greet() is a method. It belongs to the Person class and uses the object’s attribute name.
63 of 107
Calling a Method

You call a method using dot notation on an object:

python
CopyEdit
p1 = Person("Nasir")
print(p1.greet()) # Output: Hello, my name is Nasir

The self Parameter

• self refers to the current object (instance) of the class.


• It is how methods access and modify the object’s data.
• It is always the first parameter in instance methods, though you don’t pass it explicitly
when calling the method.

Types of Methods

1. Instance Methods
These are the most common type. They use self to access instance-specific data.

class Circle:
def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14 * self.radius * self.radius

2. Class Methods
These affect the class as a whole, not just a single instance. They use cls as the first
parameter and are marked with the @classmethod decorator.

class Student:
school_name = "Greenwood High"

@classmethod
def get_school(cls):
return cls.school_name

64 of 107
3. Static Methods
These don’t depend on self or cls. They behave like regular functions but are kept
inside the class for logical grouping. Marked with the @staticmethod decorator.

class MathUtils:
@staticmethod
def add(x, y):
return x + y

Summary

• Methods define what an object can do.


• They improve encapsulation, keeping data and behavior together.
• Instance, class, and static methods offer flexible ways to manage object and class
behavior.

Inheritance in Python
Inheritance is a fundamental feature of object-oriented programming that allows one class
(called the child or derived class) to inherit the attributes and methods of another class (called
the parent or base class). It promotes code reuse, modularity, and extensibility.

Why Use Inheritance?

• Avoid repetition by reusing existing code.


• Create a hierarchical relationship between classes.
• Add or extend functionality in child classes without modifying the parent.

Basic Syntax

class Parent:
def speak(self):
print("Speaking from Parent")

class Child(Parent):
def walk(self):
print("Walking from Child")

65 of 107
c = Child()
c.speak() # Inherited from Parent
c.walk() # Defined in Child

The Child class inherits the speak() method from Parent without having to define it again.
This is the power of inheritance.

The super() Function

The super() function is used inside a child class to call methods or constructors from the
parent class.

class Person:
def __init__(self, name):
self.name = name

class Student(Person):
def __init__(self, name, grade):
super().__init__(name)
self.grade = grade

Here, super().__init__(name) calls the parent constructor so we don’t have to repeat the
initialization logic.

Types of Inheritance

1. Single Inheritance
One child class inherits from one parent class.

class Animal:
pass

class Dog(Animal):

66 of 107
pass

2. Multiple Inheritance
A child class inherits from more than one parent class.

class Father:
pass

class Mother:
pass

class Child(Father, Mother):


pass

3. Multilevel Inheritance
A class inherits from a child class which in turn inherits from another parent class.

class Grandparent:
pass

class Parent(Grandparent):
pass

class Child(Parent):
pass

4. Hierarchical Inheritance
Multiple child classes inherit from the same parent class.

class Vehicle:
pass

class Car(Vehicle):
pass

class Bike(Vehicle):
pass

67 of 107
Overriding Methods

A child class can override a method from its parent by defining it with the same name.

class Parent:
def greet(self):
return "Hello from Parent"

class Child(Parent):
def greet(self):
return "Hello from Child"

c = Child()
print(c.greet()) # Output: Hello from Child

Summary

• Inheritance enables a new class to use existing code from another class.
• It fosters reusability, clarity, and hierarchical structure.
• super() is a key tool for maintaining the parent-child relationship.
• Supports different types: single, multiple, multilevel, hierarchical.

What Is Abstraction?
Abstraction is one of the four core principles of Object-Oriented Programming (OOP) — the
others being Encapsulation, Inheritance, and Polymorphism.

At its core, abstraction means:

"Expose only what is necessary, and hide the rest."

Just like when you use a mobile phone:

• You see buttons and screens (interface),


• You don’t see circuits and processors (implementation).

In programming, abstraction hides the internal logic and only exposes a user-friendly
interface that helps simplify complex systems.

68 of 107
Purpose of Abstraction
Why should you care about abstraction as a full stack developer?

• It simplifies the user experience.


• It reduces system complexity by isolating details.
• It enforces a standard structure or contract across classes.
• It allows the developer to change implementation without affecting external users.

How Is Abstraction Achieved in Python?


Python does not support abstraction in the same way as languages like Java or C++. But thanks
to the abc module (Abstract Base Classes), you can enforce abstraction in Python very
effectively.

To implement abstraction in Python:

1. Use the ABC class from the abc module.


2. Define one or more @abstractmethods inside it.
3. Create child classes that override those methods.

Step-by-Step Example
from abc import ABC, abstractmethod

# Abstract Base Class


class Vehicle(ABC):

@abstractmethod
def start_engine(self):
pass

@abstractmethod
def stop_engine(self):
pass

Here, Vehicle is an abstract class. It cannot be instantiated directly because it contains abstract
methods.

69 of 107
This will throw an error:

v = Vehicle() # Error: Can't instantiate abstract class

Create a Concrete Subclass

class Car(Vehicle):
def start_engine(self):
print("Car engine started.")

def stop_engine(self):
print("Car engine stopped.")

Now you can use it:

my_car = Car()
my_car.start_engine() # Output: Car engine started.
my_car.stop_engine() # Output: Car engine stopped.

Real-Life Example: Payment Gateway


Let’s say you’re building an e-commerce system that supports different payment methods. You
want to force every payment method to implement a pay() method.

from abc import ABC, abstractmethod

class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
pass

Now, all subclasses must define how pay() works:

class CreditCard(PaymentMethod):
def pay(self, amount):
print(f"Paid ₹{amount} using Credit Card.")

70 of 107
class UPI(PaymentMethod):
def pay(self, amount):
print(f"Paid ₹{amount} using UPI.")

class PayPal(PaymentMethod):
def pay(self, amount):
print(f"Paid ₹{amount} using PayPal.")

This allows you to enforce a consistent interface, even though the implementation differs.

Rules of Abstraction in Python


Rule Explanation
The base class for abstraction. Inherit from it to create abstract
ABC
classes.
@abstractme Decorator used to define methods that must be implemented in
thod subclasses.
Cannot
Abstract classes cannot be instantiated directly.
instantiate
Must override All abstract methods must be implemented in child classes.

When to Use Abstraction?


• When designing a system interface (e.g., database drivers, APIs, frameworks).
• When multiple classes share common behavior, but differ in implementation.
• When you want to enforce consistency across different modules.
• When you want to protect internal logic from being exposed.

Abstraction vs Encapsulation (Know the Difference)


Feature Abstraction Encapsulation
Focus Hides implementation Protects data
Purpose Simplifies user interaction Prevents unauthorized access
Achieved Private/public variables,
Abstract classes, interfaces
by getters/setters

71 of 107
You use .append() on a list, but don’t know how it works You restrict access to a class’s
Example
internally attributes

Summary
• Abstraction is about hiding complex logic and exposing only essential parts.
• Use Python’s abc module with ABC and @abstractmethod to implement it.
• Abstract classes define a contract; child classes must fulfill it.
• Abstraction makes systems clean, maintainable, and scalable.

What is Encapsulation?
Encapsulation is the process of binding data (variables) and methods (functions) that operate
on the data into a single unit—typically a class—and restricting direct access to some
components.

In simpler terms:

“Encapsulation is about hiding the internal state of an object and requiring all interactions to
be performed through well-defined interfaces.”

Just like:

• You drive a car using the steering wheel and pedals,


• But you cannot directly mess with the engine or the transmission.

That’s encapsulation—controlled interaction.

Purpose of Encapsulation
Why encapsulate? Because it gives you:

• Control over your data – Who can access or modify it?


• Security – Prevents accidental or unauthorized modifications.
• Flexibility – You can change internal code without affecting users.
• Cleaner code – Promotes modularity and separation of concerns.

72 of 107
Encapsulation in Python
Python is a flexible language, and while it doesn't enforce strict access control like Java or C++,
it still supports encapsulation using naming conventions and special syntax.

Access Modifiers in Python


Python uses a naming convention to denote access control:

Modifier Syntax Access Level


Public var Accessible from anywhere
Protecte Accessible within class & subclasses (by
_var
d convention)
Private __var Name-mangled to restrict access

Public Members

class Student:
def __init__(self):
self.name = "Nasir"

s = Student()
print(s.name) # Output: Nasir

Everything is accessible. This is fine for simple data, but not ideal for sensitive information.

Protected Members

class Student:
def __init__(self):
self._marks = 85 # Protected (by convention)

s = Student()
print(s._marks) # Technically accessible, but discouraged

73 of 107
Use _ to indicate that the attribute shouldn't be accessed directly.

Private Members

class Student:
def __init__(self):
self.__grade = "A" # Private

s = Student()
# print(s.__grade) # Error: Attribute not accessible

But Python internally uses name mangling, so the actual attribute is _Student__grade:

print(s._Student__grade) # Output: A (but you shouldn't do this!)

Getter and Setter Methods


Encapsulation isn’t just about hiding. It’s also about providing controlled access. This is where
getter and setter methods come in.

python
class BankAccount:
def __init__(self):
self.__balance = 0

def get_balance(self):
return self.__balance

def deposit(self, amount):


if amount > 0:
self.__balance += amount

def withdraw(self, amount):


if 0 < amount <= self.__balance:
self.__balance -= amount

Now, interaction is done safely:


74 of 107
account = BankAccount()
account.deposit(5000)
account.withdraw(1000)
print(account.get_balance()) # Output: 4000

This is true encapsulation: internal data is hidden, and access is controlled.

Real-Life Example: Login System


class User:
def __init__(self):
self.__password = "secret123" # Private

def change_password(self, old_pass, new_pass):


if old_pass == self.__password:
self.__password = new_pass
return "Password changed."
return "Invalid current password."

You’re not allowed to touch __password directly. You must go through the
change_password() method.

When to Use Encapsulation?


• When working with sensitive or critical data (e.g., passwords, bank balance).
• When you need to protect internal logic from being tampered with.
• When building APIs and reusable modules.
• To promote robust, modular, and secure software.

75 of 107
Summary
• Encapsulation is about bundling data and methods together and restricting direct
access.
• Python uses naming conventions: _protected, __private.
• Use getter and setter methods to access private data safely.
• Encapsulation ensures data integrity, security, and cleaner code design.

Comparison: Encapsulation vs Abstraction


Aspect Encapsulation Abstraction
Focus Protects data Hides complexity
Access modifiers,
Achieved By Abstract classes, interfaces
getters/setters
Purpose Prevents unauthorized access Simplifies interface for user
Hides implementation and
Visibility Hides implementation details
usage
Abstract method
Example Private variable __balance
make_payment()

What is Polymorphism
Polymorphism means "many shapes" in Greek, and in programming, it refers to the ability of
one interface to represent different underlying forms (data types). In simpler terms:

Polymorphism allows objects of different classes to be treated as objects of a common


superclass, with each object responding to the same method call in its own way.

It’s like a chameleon, which changes color depending on its environment, but at its core, it’s still
a chameleon.

Purpose of Polymorphism
Why should you use polymorphism? Here’s why it’s a game-changer:
76 of 107
• Flexibility – Write code that works with any class object, as long as it adheres to the
required interface.
• Reusability – Use the same method for different classes, reducing redundancy.
• Maintainability – Add new classes and behaviors without changing existing code.
• Extensibility – Easily extend functionality by creating new classes that still fit into the
system.

How Polymorphism Works in Python


In Python, polymorphism is mostly achieved through method overriding and duck typing.

• Method Overriding: A child class provides its own implementation of a method that is
already defined in its parent class.
• Duck Typing: Python’s philosophy: “If it looks like a duck and quacks like a duck, it is a
duck.” As long as an object implements the expected behavior, Python allows you to
interact with it, even if its class doesn’t explicitly match.

Example 1: Method Overriding

Let’s start with the more traditional approach to polymorphism: method overriding.

Parent Class:

class Animal:
def speak(self):
raise NotImplementedError("Subclass must implement abstract
method")

Child Classes:

class Dog(Animal):
def speak(self):
return "Bark"

class Cat(Animal):
def speak(self):
return "Meow"

class Cow(Animal):
def speak(self):

77 of 107
return "Moo"

Polymorphism in Action:

animals = [Dog(), Cat(), Cow()]

for animal in animals:


print(animal.speak()) # Output: Bark, Meow, Moo

Here:

• The method speak() is overridden in each child class, allowing each animal to speak in
its own way.
• The same method name (speak()) works differently depending on which object it is
called on. This is polymorphism in action.

Example 2: Duck Typing

Python uses duck typing, which is a form of polymorphism that focuses on an object’s
behavior rather than its class.

class Bird:
def fly(self):
return "Flying high"

class Airplane:
def fly(self):
return "Flying at 30,000 feet"

Now, both Bird and Airplane have a fly() method. We can treat them interchangeably if
we care only about the method they implement, not their class type:

def lets_fly(flyable_thing):
print(flyable_thing.fly())

bird = Bird()

78 of 107
plane = Airplane()

lets_fly(bird) # Output: Flying high


lets_fly(plane) # Output: Flying at 30,000 feet

This is duck typing: as long as an object implements the fly() method, we can use it,
regardless of its actual class.

Real-Life Example: Shapes (Another Classic)


Polymorphism can be very useful when working with shapes, as different shapes implement the
same area() method in different ways.

class Shape:
def area(self):
pass

class Circle(Shape):
def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14 * (self.radius ** 2)

class Square(Shape):
def __init__(self, side):
self.side = side

def area(self):
return self.side * self.side

Now, you can write code that works for any shape:

shapes = [Circle(5), Square(4)]

for shape in shapes:

79 of 107
print(shape.area()) # Output: 78.5, 16

Here, both Circle and Square classes have an area() method, but their implementations are
different. Thanks to polymorphism, we can use the same interface (area()) to compute areas
of different shapes without worrying about the class.

Key Takeaways from Polymorphism


• Method Overriding: Polymorphism allows child classes to override methods of the
parent class, giving each class its own unique behavior.
• Duck Typing: In Python, an object is considered to be of a certain type if it implements
the expected behavior, regardless of its actual class.
• Flexibility and Extensibility: Polymorphism allows you to write more flexible,
reusable, and maintainable code by enabling objects of different types to be treated
uniformly.

When to Use Polymorphism?


• When you want to define a common interface that different classes will implement in
their own way.
• When you want to write code that can handle multiple types of objects, but treat them in a
unified manner.
• When you're designing systems that are extendable, where new functionality can be
added by simply creating new subclasses without changing existing code.

Summary
• Polymorphism allows objects to be treated as instances of their parent class, but they can
implement their own unique behavior.
• It is achieved in Python through method overriding and duck typing.
• Method overriding enables child classes to implement methods in their own way.
• Duck typing focuses on an object’s behavior, allowing for flexibility in working with
different types.
• Polymorphism promotes reusability, maintainability, and extensibility in your code.

80 of 107
Comparison: Polymorphism vs Inheritance
Aspect Polymorphism Inheritance
Allows different classes to
Purpos Allows one class to inherit attributes
implement the same method
e and methods from another class.
differently.
Focus Behavior (methods). Structure (attributes and methods).
Key
Class inheritance (parent-child
Mecha Method overriding, duck typing
relationship)
nism
Use When you want a common interface When you want to reuse code from a
Case for different classes. parent class.

NumPy: A Comprehensive Overview

What is NumPy?
NumPy stands for Numerical Python, and it’s an open-source library that enables you to
perform high-performance mathematical and numerical computations on large datasets. It adds
support for arrays, matrices, and a vast collection of mathematical functions to perform
operations on these arrays.

• Arrays: At the core of NumPy is the ndarray (n-dimensional array). It provides a way
to store and manipulate data in a more efficient and compact form compared to Python's
built-in lists.
• Vectorization: This is the process of performing operations on arrays element-wise
without using explicit loops, which results in significant speed gains.

Key Concepts in NumPy

81 of 107
1. ndarray (n-dimensional array)

The ndarray is the heart of NumPy. It’s a multidimensional container that holds a collection of
items, all of the same type, and is indexed by a tuple of non-negative integers. NumPy’s
ndarray is more efficient and faster than Python’s native list.

Example:

import numpy as np

# 1D array
arr = np.array([1, 2, 3, 4])
print(arr) # Output: [1 2 3 4]

# 2D array
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(matrix)

2. Array Creation

NumPy provides multiple methods to create arrays.

• Using np.array(): To create an array from a Python list or tuple.


• Using np.zeros() and np.ones(): To create arrays filled with zeros or ones,
respectively.
• Using np.arange(): To create an array with evenly spaced values within a specified
range.
• Using np.linspace(): To create an array with a specified number of equally spaced
values.

Examples:

# Array of zeros
zero_arr = np.zeros((3, 3))
print(zero_arr)

# Array of ones
ones_arr = np.ones((2, 2))

82 of 107
print(ones_arr)

# Array of evenly spaced values


arange_arr = np.arange(10, 20, 2) # Start at 10, end at 20, step by 2
print(arange_arr)

3. Array Indexing and Slicing

NumPy arrays can be indexed and sliced similar to Python lists, but with added flexibility and
powerful features like multi-dimensional indexing.

Examples:

# Indexing
arr = np.array([10, 20, 30, 40, 50])
print(arr[2]) # Output: 30

# Slicing
print(arr[1:4]) # Output: [20 30 40]

# 2D array slicing
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(matrix[0:2, 1:3]) # Output: [[2 3] [5 6]]

4. Array Operations (Vectorization)

NumPy allows vectorized operations, which means you can apply mathematical operations to
entire arrays element-wise, without using loops.

Examples:

arr = np.array([1, 2, 3, 4])


print(arr * 2) # Output: [2 4 6 8]

# Array addition
arr2 = np.array([5, 6, 7, 8])

83 of 107
print(arr + arr2) # Output: [6 8 10 12]

This is a major performance advantage over using Python lists, as NumPy operations are
executed in compiled code rather than Python’s slower interpreted code.

5. Mathematical Functions

NumPy provides a wide range of mathematical functions that can be applied to arrays. These
include:

• Trigonometric functions like np.sin(), np.cos()


• Logarithmic functions like np.log(), np.exp()
• Statistical functions like np.mean(), np.median(), np.std()
• Linear algebra operations like matrix multiplication, dot product (np.dot()),
determinant (np.linalg.det())

Examples:

python
CopyEdit
# Element-wise trigonometric function
arr = np.array([0, np.pi/2, np.pi])
print(np.sin(arr)) # Output: [0. 1. 0.]

# Mean and standard deviation


data = np.array([1, 2, 3, 4, 5])
print(np.mean(data)) # Output: 3.0
print(np.std(data)) # Output: 1.4142135623730951

6. Broadcasting

Broadcasting refers to the ability to apply binary operations (like addition, multiplication)
between arrays of different shapes. NumPy automatically expands the smaller array to match
the shape of the larger array, making operations efficient.

84 of 107
Example:

arr = np.array([1, 2, 3])


matrix = np.array([[10, 20, 30], [40, 50, 60]])

# Broadcasting addition
result = matrix + arr
print(result)

The 1D array arr is automatically "broadcast" to match the shape of the 2D array matrix,
allowing element-wise addition.

7. Reshaping Arrays

NumPy provides methods to reshape arrays, which is useful when working with machine
learning algorithms or manipulating data. The shape of the array can be changed without
modifying its data.

Example:

arr = np.array([1, 2, 3, 4, 5, 6])


reshaped = arr.reshape(2, 3)
print(reshaped)

8. Random Module

NumPy includes a random module to generate random numbers, which is crucial for
simulations, statistical modeling, and machine learning.

random_arr = np.random.random((3, 3)) # Random values between 0 and 1


print(random_arr)

# Random integers
rand_int = np.random.randint(1, 10, (2, 2)) # Random integers between
1 and 10

85 of 107
print(rand_int)

Key Advantages of NumPy


1. Speed: NumPy’s array operations are implemented in C, making them much faster than
Python's native lists for numerical operations.
2. Memory Efficiency: NumPy arrays use less memory compared to Python lists, making it
ideal for large datasets.
3. Flexibility: Supports a wide variety of array manipulations, mathematical functions, and
operations.
4. Compatibility: It works seamlessly with other scientific libraries, such as SciPy, pandas,
Matplotlib, and scikit-learn.

Summary
• NumPy is a powerful library for numerical computing, built around the ndarray (n-
dimensional array) to handle large, multi-dimensional arrays and matrices.
• Key features include array creation, vectorized operations, mathematical functions,
broadcasting, and reshaping arrays.
• It significantly enhances performance by using compiled code for operations, making it
far more efficient than Python’s built-in lists.
• It integrates well with other scientific libraries, allowing Python to be used for complex
computations, machine learning, data analysis, and scientific research.

Step-by-Step Guide to Install NumPy


1. Install via pip (Python Package Installer)

The most common and recommended method for installing NumPy is by using pip, the Python
package manager. pip is bundled with Python, so it should already be available if you have
Python installed.

• Open your terminal or command prompt.


• Run the following command:

bash
pip install numpy

86 of 107
This command will download and install the latest version of NumPy from the Python Package
Index (PyPI).

2. Verify Installation

Once the installation is complete, you can verify that NumPy is successfully installed by
checking its version in Python:

bash
python -c "import numpy; print(numpy.__version__)"

If NumPy is installed correctly, this command will output the version number of NumPy, like
1.23.1 (the version may vary based on the latest release).

3. Install via Conda (If using Anaconda or Miniconda)

If you are using the Anaconda or Miniconda distribution, you can install NumPy via the conda
package manager, which is optimized for scientific computing and automatically manages
dependencies for you.

• Open the Anaconda Prompt or terminal.


• Run the following command:

bash
conda install numpy

Conda will automatically fetch the appropriate version of NumPy and its dependencies, ensuring
everything works smoothly in the environment.

4. Installing in a Virtual Environment (Recommended)

It's a good practice to install packages in a virtual environment to avoid conflicts between
different projects. You can use venv (built-in for Python 3.3 and above) to create isolated
environments.

Steps:

1. Create a new virtual environment:

bash

87 of 107
python -m venv myenv

2. Activate the virtual environment:


a. Windows:

bash
myenv\Scripts\activate

b. macOS/Linux:

bash
source myenv/bin/activate

3. Install NumPy in the virtual environment:

bash
pip install numpy

4. Deactivate the virtual environment when done:

bash
deactivate

Alternative Install Methods


Install from Source

If you want to install NumPy from source (which is less common for most users), you can
download the source code from GitHub or the official NumPy website and install it manually.

1. Clone the NumPy repository from GitHub:

bash
git clone https://github.com/numpy/numpy.git

88 of 107
2. Navigate to the NumPy directory:

bash
cd numpy

3. Build and install:

bash
python setup.py install

This method is typically used by developers or users with specific needs, such as contributing to
NumPy or using a custom version.

Updating NumPy
To ensure you have the latest version of NumPy, you can update it using the following pip
command:

bash
pip install --upgrade numpy

For users with conda, you can update using:

bash
conda update numpy

Troubleshooting
• Issue 1: pip not found
If you get an error saying pip is not recognized, make sure Python and pip are added to
your system’s PATH variable.
o You can reinstall Python and check the box that says Add Python to PATH
during the installation.
• Issue 2: Version Conflict
Sometimes, other installed packages may conflict with NumPy. Consider using a virtual

89 of 107
environment to isolate dependencies for each project, or run pip check to identify and
resolve conflicts.

Conclusion
Installing NumPy is easy and can be done via pip or conda in most cases. By following these
steps, you should be able to set up NumPy in your Python environment and start using its
powerful array manipulation and mathematical capabilities. If you run into any issues, consider
using a virtual environment for better dependency management.

Creating Arrays in NumPy (1D, 2D, and 3D)

NumPy provides a variety of methods to create arrays of different dimensions. Arrays in NumPy
are the backbone for numerical operations, and you can create them for 1D (one-dimensional),
2D (two-dimensional), and 3D (three-dimensional) purposes.

Let’s break down the process of creating arrays for each dimension with examples.

1. Creating a 1D Array
A 1D array is simply a list of values in a single row, like a vector.

Syntax:

python
import numpy as np
arr = np.array([1, 2, 3, 4, 5])

Example:

python
import numpy as np

# Creating a 1D array
arr_1d = np.array([10, 20, 30, 40, 50])

# Display the array

90 of 107
print("1D Array:")
print(arr_1d)

Output:

1D Array:
[10 20 30 40 50]

2. Creating a 2D Array
A 2D array is a matrix, which consists of rows and columns.

Syntax:

python
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])

Example:

python
import numpy as np

# Creating a 2D array
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])

# Display the array


print("2D Array:")
print(arr_2d)

Output:

2D Array:
[[1 2 3]
[4 5 6]]

91 of 107
3. Creating a 3D Array
A 3D array can be thought of as a stack of matrices (multiple 2D arrays). It has depth in addition
to rows and columns.

Syntax:

python
arr_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

Example:

python
import numpy as np

# Creating a 3D array
arr_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

# Display the array


print("3D Array:")
print(arr_3d)

Output:

lua
3D Array:
[[[1 2]
[3 4]]

[[5 6]
[7 8]]]

92 of 107
Alternative Methods for Array Creation
Using np.zeros() (Creates an array filled with zeros)

• 1D:

python
arr_1d_zeros = np.zeros(5) # 1D array with 5 zeros

• 2D:

python
arr_2d_zeros = np.zeros((3, 3)) # 2D array (3x3) with zeros

• 3D:

python
arr_3d_zeros = np.zeros((2, 3, 2)) # 3D array (2x3x2) with zeros

Using np.ones() (Creates an array filled with ones)

• 1D:

python
arr_1d_ones = np.ones(4) # 1D array with 4 ones

• 2D:

python
arr_2d_ones = np.ones((2, 4)) # 2D array (2x4) with ones

• 3D:

python
arr_3d_ones = np.ones((3, 2, 2)) # 3D array (3x2x2) with ones

93 of 107
Using np.arange() (Creates an array with a sequence of numbers)

• 1D:

python
arr_1d_range = np.arange(1, 11, 2) # 1D array from 1 to 10, step 2

• 2D:

python
arr_2d_range = np.arange(1, 13).reshape(3, 4) # 2D array (3x4)

• 3D:

python
CopyEdit
arr_3d_range = np.arange(1, 25).reshape(2, 3, 4) # 3D array (2x3x4)

Conclusion
• 1D Arrays are essentially vectors, storing a sequence of values in one row.
• 2D Arrays are matrices, with rows and columns.
• 3D Arrays are a stack of 2D arrays, adding depth to the dimensions.

Indexing and Slicing in NumPy Arrays

Indexing and slicing are fundamental operations when working with NumPy arrays. They allow
you to access and modify specific elements or subarrays in an array. Let's explore how indexing
and slicing work in NumPy with clear explanations and examples.

1. Indexing in NumPy Arrays


Indexing allows you to access individual elements in a NumPy array. In NumPy, arrays can be
indexed in a similar way to regular Python lists, but NumPy arrays support additional features
such as multi-dimensional indexing.
94 of 107
Basic Indexing (1D Arrays)

In a 1D array, you can access elements by specifying their index. Indexing starts from 0.

Syntax:

python
CopyEdit
array[index]

Example:

python
CopyEdit
import numpy as np

# 1D Array
arr = np.array([10, 20, 30, 40, 50])

# Accessing elements by index


print("Element at index 2:", arr[2]) # Output: 30
print("Element at index -1:", arr[-1]) # Output: 50 (last element)

Output:

perl
CopyEdit
Element at index 2: 30
Element at index -1: 50

Indexing in 2D Arrays

For 2D arrays, you need to specify two indices: one for the row and one for the column. You
use array[row, column] to access elements.

95 of 107
Syntax:

python
CopyEdit
array[row_index, column_index]

Example:

python
CopyEdit
import numpy as np

# 2D Array
arr_2d = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])

# Accessing elements
print("Element at row 1, column 2:", arr_2d[1, 2]) # Output: 60
print("Element at row 0, column 0:", arr_2d[0, 0]) # Output: 10

Output:

sql
CopyEdit
Element at row 1, column 2: 60
Element at row 0, column 0: 10

Indexing in 3D Arrays

For 3D arrays, you need to specify three indices: one for the depth, one for the row, and one for
the column.

Syntax:

python
CopyEdit

96 of 107
array[depth_index, row_index, column_index]

Example:

python
CopyEdit
import numpy as np

# 3D Array
arr_3d = np.array([[[10, 20], [30, 40]], [[50, 60], [70, 80]]])

# Accessing elements
print("Element at depth 1, row 1, column 1:", arr_3d[1, 1, 1]) #
Output: 80
print("Element at depth 0, row 0, column 1:", arr_3d[0, 0, 1]) #
Output: 20

Output:

sql
CopyEdit
Element at depth 1, row 1, column 1: 80
Element at depth 0, row 0, column 1: 20

2. Slicing in NumPy Arrays


Slicing is a way of accessing a subset of elements from an array. In NumPy, slicing works
similarly to Python lists, but NumPy arrays allow for more advanced slicing, especially with
multi-dimensional arrays.

Basic Slicing (1D Arrays)

In 1D arrays, slicing allows you to extract a portion of the array using the start:stop:step
format.

97 of 107
Syntax:

array[start:stop:step]

• start: The index from where the slice begins (inclusive).


• stop: The index where the slice ends (exclusive).
• step: The interval between indices.

Example:

import numpy as np

# 1D Array
arr = np.array([10, 20, 30, 40, 50, 60, 70])

# Slicing the array


print("Slice from index 2 to 5:", arr[2:5]) # Output: [30 40 50]
print("Slice from the beginning to index 4:", arr[:4]) # Output: [10
20 30 40]
print("Slice from index 2 to end with step 2:", arr[2::2]) # Output:
[30 50 70]

Output:

Slice from index 2 to 5: [30 40 50]


Slice from the beginning to index 4: [10 20 30 40]
Slice from index 2 to end with step 2: [30 50 70]

Slicing in 2D Arrays

In 2D arrays, you can slice rows and columns using the array[start_row:end_row,
start_column:end_column] syntax.

98 of 107
Syntax:

array[start_row:end_row, start_column:end_column]

Example:

import numpy as np

# 2D Array
arr_2d = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])

# Slicing rows and columns


print("Slice of rows 1 to 2 and columns 0 to 2:")
print(arr_2d[1:3, 0:2]) # Output: [[40 50], [70 80]]

# Slicing a specific row


print("Slice of row 1:")
print(arr_2d[1]) # Output: [40 50 60]

Output:

Slice of rows 1 to 2 and columns 0 to 2:


[[40 50]
[70 80]]
Slice of row 1:
[40 50 60]

Slicing in 3D Arrays

For 3D arrays, you can slice in all three dimensions. The general syntax is
array[start_depth:end_depth, start_row:end_row,
start_column:end_column].

99 of 107
Syntax:

array[start_depth:end_depth, start_row:end_row,
start_column:end_column]

Example:

import numpy as np

# 3D Array
arr_3d = np.array([[[10, 20], [30, 40]], [[50, 60], [70, 80]]])

# Slicing a part of the 3D array


print("Slice of depth 0, rows 0 to 1, columns 0 to 1:")
print(arr_3d[0, 0:2, 0:2]) # Output: [[10 20] [30 40]]

Output:

Slice of depth 0, rows 0 to 1, columns 0 to 1:


[[10 20]
[30 40]]

3. Advanced Indexing and Slicing


NumPy also supports fancy indexing, which allows you to use an array of indices to access
multiple elements. It’s useful when you want to extract specific elements from an array based on
multiple indices or conditions.

Example:

import numpy as np

# 1D Array
arr = np.array([10, 20, 30, 40, 50, 60, 70])

# Fancy indexing
indices = [1, 3, 5]
print("Fancy indexing result:", arr[indices]) # Output: [20 40 60]

100 of 107
Output:

less
Fancy indexing result: [20 40 60]

Conclusion
• Indexing is used to access specific elements in an array, whether it's a single element or a
multi-dimensional element.
• Slicing allows you to extract subarrays or specific ranges of elements from arrays. You
can slice arrays using the start:stop:step notation.
• Advanced Indexing (or fancy indexing) allows you to use arrays of indices or boolean
masks to select specific elements from an array.

1. Addition of NumPy Arrays


Array addition allows you to add two arrays of the same shape element-wise. If the shapes are
not compatible, NumPy will attempt broadcasting to make them compatible.

Syntax:

array1 + array2

Example:

import numpy as np

# Two 1D Arrays
arr1 = np.array([10, 20, 30])
arr2 = np.array([5, 15, 25])

# Element-wise addition
result = arr1 + arr2
print("Addition of arrays:", result) # Output: [15 35 55]

101 of 107
Output:

Addition of arrays: [15 35 55]

2. Subtraction of NumPy Arrays


Array subtraction is used to subtract corresponding elements of two arrays.

Syntax:

array1 - array2

Example:

import numpy as np

# Two 1D Arrays
arr1 = np.array([10, 20, 30])
arr2 = np.array([5, 15, 25])

# Element-wise subtraction
result = arr1 - arr2
print("Subtraction of arrays:", result) # Output: [ 5 5 5]

Output:

Subtraction of arrays: [ 5 5 5]

3. Multiplication of NumPy Arrays


Element-wise multiplication can be performed on arrays, which means multiplying
corresponding elements from each array.

array1 * array2

102 of 107
Example:

import numpy as np

# Two 1D Arrays
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

# Element-wise multiplication
result = arr1 * arr2
print("Multiplication of arrays:", result) # Output: [ 4 10 18]

Output:

Multiplication of arrays: [ 4 10 18]

4. Division of NumPy Arrays


Division of arrays performs element-wise division. It divides each element of the first array by
the corresponding element of the second array.

Syntax:

array1 / array2

Example:

import numpy as np

# Two 1D Arrays
arr1 = np.array([10, 20, 30])
arr2 = np.array([2, 4, 5])

# Element-wise division
result = arr1 / arr2

103 of 107
print("Division of arrays:", result) # Output: [ 5. 5. 6.]

Output:

Division of arrays: [5. 5. 6.]

5. Scalar Operations (with Single Values)


You can also perform arithmetic operations between a NumPy array and a scalar (a single
number). These operations are applied element-wise to the entire array.

Example:

import numpy as np

# 1D Array
arr = np.array([10, 20, 30])

# Scalar addition
result_add = arr + 5
print("Scalar addition:", result_add) # Output: [15 25 35]

# Scalar subtraction
result_sub = arr - 5
print("Scalar subtraction:", result_sub) # Output: [ 5 15 25]

# Scalar multiplication
result_mul = arr * 2
print("Scalar multiplication:", result_mul) # Output: [20 40 60]

# Scalar division
result_div = arr / 5
print("Scalar division:", result_div) # Output: [2. 4. 6.]

104 of 107
Output:

Scalar addition: [15 25 35]


Scalar subtraction: [ 5 15 25]
Scalar multiplication: [20 40 60]
Scalar division: [2. 4. 6.]

6. Broadcasting in NumPy Arithmetic


Broadcasting refers to the ability of NumPy to perform arithmetic operations on arrays of
different shapes. When the shapes are not identical, NumPy automatically "broadcasts" the
smaller array across the larger array so that they have compatible shapes.

Example:

import numpy as np

# 2D Array and a 1D Array


arr2d = np.array([[1, 2, 3], [4, 5, 6]])
arr1d = np.array([10, 20, 30])

# Broadcasting in addition (1D array is broadcast across 2D array)


result = arr2d + arr1d
print("Result after broadcasting addition:\n", result)

Output:

Result after broadcasting addition:


[[11 22 33]
[14 25 36]]

In the above example, the 1D array arr1d is broadcast across each row of the 2D array arr2d
to perform the element-wise addition.

105 of 107
7. Universal Functions (ufuncs)
NumPy provides universal functions (ufuncs) to perform arithmetic operations on arrays. These
functions allow you to apply operations to entire arrays in a vectorized manner, ensuring fast
execution.

Example with np.add(), np.subtract(), np.multiply(), and


np.divide():

import numpy as np

# Two 1D Arrays
arr1 = np.array([10, 20, 30])
arr2 = np.array([5, 15, 25])

# Using ufuncs for addition


result_add = np.add(arr1, arr2)
print("Addition using ufunc:", result_add)

# Using ufuncs for subtraction


result_sub = np.subtract(arr1, arr2)
print("Subtraction using ufunc:", result_sub)

# Using ufuncs for multiplication


result_mul = np.multiply(arr1, arr2)
print("Multiplication using ufunc:", result_mul)

# Using ufuncs for division


result_div = np.divide(arr1, arr2)
print("Division using ufunc:", result_div)

Output:

Addition using ufunc: [15 35 55]


Subtraction using ufunc: [ 5 5 5]
Multiplication using ufunc: [ 50 300 750]
Division using ufunc: [2. 1.33333333 1.2]

106 of 107
Conclusion

• Addition, subtraction, multiplication, and division in NumPy are performed element-


wise across arrays, and you can also apply these operations to scalars.
• Broadcasting ensures that arrays of different shapes can still be combined through
arithmetic operations if they are compatible.
• Universal functions (ufuncs) in NumPy make it possible to perform these operations in
a vectorized, efficient manner.

107 of 107

You might also like