Python Tutorial with examples

🐍 Python Syllabus (Beginner → Advanced)

1. Beginner Level

  • Introduction
    • What is Python, installation, IDEs
    • Running Python scripts
  • Basics
    • Syntax, keywords, indentation
    • Variables & data types
    • Input/Output
    • Operators (arithmetic, relational, logical)
  • Control Flow
    • Conditional statements (if, elif, else)
    • Loops (for, while)
    • Loop control (break, continue, pass)
  • Collections
    • Lists, tuples, sets, dictionaries
    • Indexing, slicing, methods

2. Intermediate Level

  • Functions
    • Defining functions, arguments, return values
    • Default & keyword arguments
    • Lambda functions
  • Modules & Packages
    • Importing modules
    • Standard libraries overview
  • Error Handling
    • Exceptions (try, except, finally)
    • Custom exceptions
  • File Handling
    • Reading/writing files
    • Working with CSV, JSON
  • Object-Oriented Programming
    • Classes & objects
    • Constructors, methods
    • Inheritance, polymorphism, encapsulation

3. Advanced Level

  • Iterators & Generators
    • iter(), next()
    • yield keyword
  • Decorators
    • Function decorators
    • Class decorators
  • Recursion
    • Recursive functions
    • Applications (factorial, Fibonacci, Tower of Hanoi)
  • Data Structures & Algorithms
    • Searching & sorting
    • Linked lists, stacks, queues
  • Advanced OOP
    • Abstract classes
    • Multiple inheritance
  • Modules for Productivity
    • os, sys, datetime, collections
    • re (regular expressions)
  • Python for Automation
    • Web scraping (requests, BeautifulSoup)
    • Scripting tasks
  • Python for Data Handling
    • NumPy, Pandas basics
    • Data visualization (Matplotlib, Seaborn)

4. Expert / Specialized Topics

  • Concurrency
    • Multithreading & multiprocessing
    • Async programming (asyncio)
  • Advanced Libraries
    • Flask/Django (web development)
    • TensorFlow/PyTorch (machine learning)
  • Testing
    • Unit testing (unittest, pytest)
  • Deployment
    • Virtual environments
    • Packaging & distribution
  • Best Practices
    • Code style (PEP 8)
    • Documentation
    • Version control (Git)

1. What is Python?

  • High-level language: Easy to read and write, close to human language.
  • Interpreted: Code runs line by line, no need for compilation.
  • Versatile: Used in web development, data science, AI, automation, and more.
  • Cross-platform: Works on Windows, macOS, and Linux.

2. Installing Python

Windows

  1. Go to python.org/downloads.
  2. Download the latest version (Python 3.x).
  3. Run the installer → check “Add Python to PATH”.
  4. Click Install Now.
  5. Verify installation:
    python --version
    
    or
    python
    

macOS

  • Python 3 may already be installed.
  • To install latest:
    brew install python
    
    (requires Homebrew).
  • Verify:
    python3 --version
    

Linux

  • Most distros ship with Python.

  • To install/update:

    sudo apt update
    sudo apt install python3
    
  • Verify:

    python3 --version
    


3. Setting Up IDEs

Popular IDEs for Python:

  • VS Code: Lightweight, extensions for Python debugging, linting.
  • PyCharm: Full-featured IDE, great for large projects.
  • Jupyter Notebook: Ideal for data science, interactive coding.
  • IDLE: Comes bundled with Python, simple for beginners.

Steps:

  1. Install your chosen IDE.
  2. Configure Python interpreter (point IDE to your installed Python).
  3. Add extensions/plugins (e.g., Python extension in VS Code).


4. Running Python Scripts

Method 1: Command Line

  • Save file as hello.py:
    print("Hello, World!")
    
  • Run:
    python hello.py
    

Method 2: IDE

  • Open file in VS Code/PyCharm.
  • Click Run or use shortcut (Shift+F10 in PyCharm, Ctrl+F5 in VS Code).

Method 3: Interactive Mode

  • Open terminal → type:
    python
    
  • Enter commands directly:
    >>> print("Hello")
    

Perfect 👌 Let’s build Section 1: Python Fundamentals in a detailed, beginner-friendly way — step by step with examples.

This can also help you if you're creating YouTube tutorials or an ebook (like your JavaScript ebook idea 😉).


🐍 Section 1: Python Fundamentals


1️⃣ Syntax (Basic Python Programming Syntax)

🔹 What is Syntax?

Syntax means rules of writing code in a programming language.

Python is:

  • Simple

  • Readable

  • Uses indentation (very important!)


🔹 First Python Program

print("Hello, World!")

Output:

Hello, World!

🔹 Indentation (Very Important in Python)

Python uses indentation instead of {}.

if 5 > 2:
    print("Five is greater than two")

❌ Wrong:

if 5 > 2:
print("Five is greater than two")  # IndentationError

✔ Indentation defines blocks of code.


🔹 Python is Case-Sensitive

name = "Hari"
Name = "Mohan"

print(name)  # Hari
print(Name)  # Mohan

These are two different variables.


2️⃣ Variables

🔹 What is a Variable?

A variable is a container to store data.


🔹 Creating Variables

name = "Hari"
age = 25
salary = 50000.50

No need to declare type explicitly.


🔹 Rules for Naming Variables

✔ Can contain:

  • Letters (a-z, A-Z)

  • Numbers (0-9)

  • Underscore (_)

❌ Cannot:

  • Start with number

  • Use special characters

  • Use Python keywords

Valid:

user_name = "Hari"
age1 = 25

Invalid:

1name = "Hari"   # Error
user-name = "Hari"  # Error

🔹 Multiple Assignment

a, b, c = 1, 2, 3
print(a, b, c)

🔹 Dynamic Typing

x = 10
x = "Hari"

Python allows changing type dynamically.


3️⃣ Strings

🔹 What is a String?

A string is a sequence of characters.

name = "Hari"
message = 'Hello World'

🔹 Multi-line Strings

text = """This is
a multi-line
string"""

🔹 String Operations

Concatenation

first = "Hari"
last = "Mohan"

full = first + " " + last
print(full)

Repetition

print("Hi " * 3)

Output:

Hi Hi Hi

String Length

print(len("Hari"))

Indexing

name = "Python"
print(name[0])  # P
print(name[-1]) # n

Slicing

print(name[0:3])  # Pyt

Useful String Methods

text = "python"

print(text.upper())
print(text.capitalize())
print(text.replace("p", "P"))

4️⃣ Numbers

Python mainly has:

  1. int

  2. float

  3. complex


🔹 Integers

x = 10
y = -5

🔹 Float

price = 99.99
pi = 3.14

🔹 Arithmetic Operators

a = 10
b = 3

print(a + b)  # 13
print(a - b)  # 7
print(a * b)  # 30
print(a / b)  # 3.333
print(a // b) # 3
print(a % b)  # 1
print(a ** b) # 1000

5️⃣ Booleans

🔹 Boolean Type

Boolean has only two values:

  • True

  • False

is_active = True
is_admin = False

🔹 Comparison Operators

print(5 > 3)   # True
print(5 == 3)  # False
print(5 != 3)  # True

🔹 Truthy and Falsy Values

🔻 Falsy Values:

  • 0

  • "" (empty string)

  • None

  • False

  • []

  • {}

if "":
    print("True")
else:
    print("False")  # This will run

6️⃣ Constants

Python doesn't have built-in constants, but by convention we use:

🔹 UPPERCASE variable names

PI = 3.14159
MAX_USERS = 100

Python won’t stop you from changing it, but it signals:
👉 “Do not modify”


7️⃣ Comments

Comments are notes for developers.


🔹 Single-line Comment

# This is a comment
print("Hello")

🔹 Multi-line Comment

"""
This is a 
multi-line comment
"""

8️⃣ Type Conversion (Type Casting)

Type conversion means changing one data type into another.


🔹 Convert String to Integer

age = "25"
age = int(age)

print(age + 5)  # 30

🔹 Convert Integer to String

num = 100
text = str(num)

print("Number is " + text)

🔹 Convert to Float

x = "10.5"
y = float(x)

print(y)

🔹 Check Type

x = 10
print(type(x))

🔥 Mini Practice

name = input("Enter your name: ")
age = int(input("Enter your age: "))

print("Hello", name)
print("Next year you will be", age + 1)

📌 Summary

ConceptWhat You Learned
SyntaxIndentation, case sensitivity
VariablesDynamic typing
StringsIndexing, slicing, methods
Numbersint, float, operators
BooleansTrue/False, truthy/falsy
ConstantsUPPERCASE convention
CommentsCode documentation
Type Conversionint(), str(), float()

🐍 Section 2: Operators in Python

Operators are symbols used to perform operations on variables and values.

Example:

5 + 3

Here + is an operator.


1️⃣ Arithmetic Operators

Used to perform mathematical calculations.


🔹 List of Arithmetic Operators

OperatorMeaningExample
+Addition5 + 2
-Subtraction5 - 2
*Multiplication5 * 2
/Division (float result)5 / 2
//Floor Division5 // 2
%Modulus (remainder)5 % 2
**Exponent (power)5 ** 2

🔹 Addition (+)

a = 10
b = 5
print(a + b)

Output:

15

🔹 Subtraction (-)

print(10 - 3)

Output:

7

🔹 Multiplication (*)

print(4 * 3)

Output:

12

🔹 Division (/)

Always returns float.

print(10 / 2)   # 5.0
print(5 / 2)    # 2.5

🔹 Floor Division (//)

Removes decimal part.

print(5 // 2)   # 2

🔹 Modulus (%)

Returns remainder.

print(10 % 3)   # 1

Common use:

  • Check even/odd

num = 4
if num % 2 == 0:
    print("Even")

🔹 Exponent (**)

print(2 ** 3)  # 8

🔹 Operator Precedence (Important)

print(2 + 3 * 4)

Output:

14

Because:

  • Multiplication runs before addition.

Use parentheses:

print((2 + 3) * 4)

Output:

20

2️⃣ Assignment Operators

Used to assign values to variables.


🔹 Basic Assignment (=)

x = 10

🔹 Compound Assignment Operators

OperatorMeaningExample
+=Add and assignx += 5
-=Subtract and assignx -= 5
*=Multiply and assignx *= 5
/=Divide and assignx /= 5
//=Floor divide assignx //= 5
%=Modulus assignx %= 5
**=Power assignx **= 2

🔹 Example

x = 10
x += 5
print(x)  # 15

Equivalent to:

x = x + 5

🔹 More Examples

x = 10
x -= 3
print(x)  # 7

x *= 2
print(x)  # 14

x /= 2
print(x)  # 7.0

3️⃣ Comparison Operators

Used to compare two values.

Return type: Boolean (True or False)


🔹 List of Comparison Operators

OperatorMeaning
==Equal to
!=Not equal
>Greater than
<Less than
>=Greater than or equal
<=Less than or equal

🔹 Examples

print(5 == 5)   # True
print(5 != 3)   # True
print(5 > 3)    # True
print(5 < 3)    # False
print(5 >= 5)   # True
print(5 <= 3)   # False

🔹 Comparing Strings

print("apple" == "apple")   # True
print("apple" > "banana")   # False

Comparison is based on ASCII / Unicode values.


🔹 Chained Comparisons (Python Special Feature)

x = 10
print(5 < x < 20)

Output:

True

Equivalent to:

5 < x and x < 20

4️⃣ Logical Operators

Used to combine multiple conditions.


🔹 List of Logical Operators

OperatorMeaning
andTrue if both conditions are True
orTrue if at least one condition is True
notReverses condition

🔹 Logical AND

age = 25
has_license = True

if age > 18 and has_license:
    print("You can drive")

Both conditions must be True.


🔹 Logical OR

is_admin = False
is_superuser = True

if is_admin or is_superuser:
    print("Access granted")

At least one must be True.


🔹 Logical NOT

is_logged_in = False

if not is_logged_in:
    print("Please login")

not False → True


🔥 Truth Table

AND

ABA and B
TTT
TFF
FTF
FFF

OR

ABA or B
TTT
TFT
FTT
FFF

NOT

Anot A
TrueFalse
FalseTrue

🔥 Short-Circuiting (Advanced Concept)

Python stops evaluation early.

Example:

False and print("Hello")

Nothing prints because:

  • First condition is False

  • AND already False → stops


🔥 Practical Example

username = "hari"
password = "1234"

if username == "hari" and password == "1234":
    print("Login successful")
else:
    print("Invalid credentials")

📌 Summary

CategoryPurpose
ArithmeticMath operations
AssignmentStore & update values
ComparisonCompare values
LogicalCombine conditions



Perfect 👌
Now let’s build Section 3: Control Flow in Python — detailed, structured, beginner → intermediate level with examples and explanations.


🐍 Section 3: Control Flow

Control flow allows your program to:

  • Make decisions

  • Repeat actions

  • Control execution order


1️⃣ if…else Statement

Used to execute code based on a condition.


🔹 Basic Syntax

if condition:
    # code block

🔹 Simple Example

age = 20

if age >= 18:
    print("You are an adult")

If condition is True, block executes.


🔹 if…else

age = 16

if age >= 18:
    print("Adult")
else:
    print("Minor")

If condition is False → else runs.


🔹 if…elif…else

Used when checking multiple conditions.

marks = 75

if marks >= 90:
    print("Grade A")
elif marks >= 70:
    print("Grade B")
elif marks >= 50:
    print("Grade C")
else:
    print("Fail")

Python checks conditions from top to bottom.


🔹 Nested if

age = 25
has_license = True

if age >= 18:
    if has_license:
        print("You can drive")
    else:
        print("You need a license")

2️⃣ Ternary Operator (Conditional Expression)

Makes code shorter and cleaner.


🔹 Syntax

value_if_true if condition else value_if_false

🔹 Example

Normal way:

age = 20

if age >= 18:
    result = "Adult"
else:
    result = "Minor"

Ternary way:

age = 20
result = "Adult" if age >= 18 else "Minor"

print(result)

🔹 Another Example

num = 10
print("Even") if num % 2 == 0 else print("Odd")

⚠️ Best practice:
Use ternary only for simple conditions.


3️⃣ for Loop with range()

Used when number of iterations is known.


🔹 Basic Syntax

for variable in range(start, stop, step):
    # code block

🔹 range() Variations

1. Only stop

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

Output:

0
1
2
3
4

Default start = 0


2. Start and Stop

for i in range(1, 6):
    print(i)

Output:

1
2
3
4
5

3. Start, Stop, Step

for i in range(0, 10, 2):
    print(i)

Output:

0
2
4
6
8

🔹 Loop Through String

name = "Hari"

for char in name:
    print(char)

🔹 Example: Sum of First 5 Numbers

total = 0

for i in range(1, 6):
    total += i

print(total)

Output:

15

4️⃣ while Loop

Used when number of iterations is not fixed.

Runs as long as condition is True.


🔹 Basic Syntax

while condition:
    # code block

🔹 Example

count = 1

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

⚠️ Important:
Always update condition variable.

Otherwise → Infinite Loop.


🔹 Infinite Loop Example

while True:
    print("Running...")

Used in:

  • Game loops

  • Servers

  • Continuous processes


5️⃣ break Statement

Used to exit loop immediately.


🔹 Example with for

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

Output:

0
1
2
3
4

Loop stops at 5.


🔹 Example with while

count = 1

while count <= 10:
    if count == 6:
        break
    print(count)
    count += 1

6️⃣ continue Statement

Skips current iteration and moves to next.


🔹 Example

for i in range(5):
    if i == 2:
        continue
    print(i)

Output:

0
1
3
4

When i == 2 → skipped.


🔹 Practical Example: Print Only Even Numbers

for i in range(10):
    if i % 2 != 0:
        continue
    print(i)

🔥 break vs continue

StatementWhat it Does
breakStops entire loop
continueSkips current iteration

🔥 for vs while

for loopwhile loop
Known iterationsUnknown iterations
Uses range()Uses condition
Cleaner for countingGood for dynamic cases

🔥 Real-world Example

Password check:

correct_password = "1234"

while True:
    password = input("Enter password: ")
    
    if password == correct_password:
        print("Access Granted")
        break
    else:
        print("Try again")

📌 Summary

ConceptPurpose
if / elseDecision making
TernaryShort conditional
for loopFixed repetitions
while loopCondition-based repetition
breakExit loop
continueSkip iteration

Perfect 👌
Let’s understand pass statement in Python in detail.


🐍 The pass Statement in Python

🔹 What is pass?

pass is a null statement.

It does nothing.

It is used as a placeholder when Python requires a statement but you don’t want to write code yet.


🔹 Why Do We Need pass?

Python does not allow empty code blocks.

This ❌ will cause an error:

if True:

Error:

IndentationError: expected an indented block

So we use pass to avoid syntax error.


1️⃣ Using pass in if Statement

age = 18

if age >= 18:
    pass

This runs without error, but does nothing.


2️⃣ Using pass in Loops

Sometimes we create a loop but implement logic later.

for i in range(5):
    pass

Loop runs but no output.


Example: Empty while Loop

while True:
    pass

⚠️ Warning: This creates an infinite loop that does nothing (uses CPU).


3️⃣ Using pass in Functions

When defining a function structure first:

def my_function():
    pass

Useful when:

  • Designing structure first

  • Writing skeleton code

  • Planning large projects


4️⃣ Using pass in Classes

class Person:
    pass

Creates an empty class.


5️⃣ Practical Real-world Example

Imagine you're building a login system and want to implement later:

username = "admin"

if username == "admin":
    pass  # Add admin logic later
else:
    print("Access Denied")

🔥 pass vs continue vs break

StatementWhat it Does
passDoes nothing
continueSkips current loop iteration
breakStops loop completely

Example to Compare

for i in range(5):
    if i == 2:
        pass
    print(i)

Output:

0
1
2
3
4

Because pass does nothing.


Now with continue:

for i in range(5):
    if i == 2:
        continue
    print(i)

Output:

0
1
3
4

🔥 When Should You Use pass?

✔ While designing code structure
✔ Creating empty functions/classes
✔ Avoiding syntax errors
✔ Planning logic later

❌ Not for skipping logic permanently


📌 Summary

  • pass = placeholder

  • Used when block cannot be empty

  • Common in if, loops, functions, classes

  • Does absolutely nothing



🐍 Python Lists

🔹 What is a List?

A list is a collection that:

  • Stores multiple values

  • Is ordered

  • Is changeable (mutable)

  • Allows duplicate values


🔹 Creating a List

numbers = [1, 2, 3, 4, 5]
names = ["Hari", "Mohan", "Python"]
mixed = [10, "Hello", True, 5.5]

🔹 Accessing List Elements

Indexing

names = ["Hari", "Mohan", "Python"]

print(names[0])   # Hari
print(names[-1])  # Python

Slicing

numbers = [1, 2, 3, 4, 5]

print(numbers[1:4])  # [2, 3, 4]

🔹 Changing List Elements

numbers = [1, 2, 3]
numbers[0] = 100

print(numbers)  # [100, 2, 3]

🔹 Loop Through List

for item in numbers:
    print(item)

📌 Important List Methods


1️⃣ append()

Adds element at the end.

fruits = ["apple", "banana"]
fruits.append("mango")

print(fruits)

Output:

['apple', 'banana', 'mango']

2️⃣ insert()

Insert element at specific index.

fruits = ["apple", "banana"]
fruits.insert(1, "orange")

print(fruits)

Output:

['apple', 'orange', 'banana']

3️⃣ extend()

Adds multiple elements (another list).

a = [1, 2]
b = [3, 4]

a.extend(b)

print(a)

Output:

[1, 2, 3, 4]

4️⃣ remove()

Removes first matching value.

numbers = [1, 2, 3, 2]
numbers.remove(2)

print(numbers)

Output:

[1, 3, 2]

5️⃣ pop()

Removes element by index (default: last).

numbers = [10, 20, 30]
numbers.pop()

print(numbers)

Output:

[10, 20]

Remove specific index:

numbers.pop(0)

6️⃣ clear()

Removes all elements.

numbers = [1, 2, 3]
numbers.clear()

print(numbers)  # []

7️⃣ index()

Returns index of first match.

names = ["Hari", "Mohan", "Python"]
print(names.index("Mohan"))

Output:

1

8️⃣ count()

Counts occurrences.

numbers = [1, 2, 2, 3, 2]
print(numbers.count(2))

Output:

3

9️⃣ sort()

Sorts list (ascending by default).

numbers = [4, 1, 3, 2]
numbers.sort()

print(numbers)

Output:

[1, 2, 3, 4]

Descending:

numbers.sort(reverse=True)

🔟 reverse()

Reverses list order.

numbers = [1, 2, 3]
numbers.reverse()

print(numbers)

Output:

[3, 2, 1]

1️⃣1️⃣ copy()

Creates shallow copy.

a = [1, 2, 3]
b = a.copy()

print(b)

🔥 Built-in Functions Useful with Lists


len()

print(len([1, 2, 3]))

max() / min()

numbers = [10, 20, 5]

print(max(numbers))  # 20
print(min(numbers))  # 5

sum()

numbers = [1, 2, 3]
print(sum(numbers))  # 6

🔥 Nested Lists

matrix = [
    [1, 2, 3],
    [4, 5, 6]
]

print(matrix[0][1])  # 2

🔥 List Comprehension (Important)

Short way to create lists.

Normal way:

squares = []
for i in range(5):
    squares.append(i * i)

List comprehension:

squares = [i * i for i in range(5)]
print(squares)

Output:

[0, 1, 4, 9, 16]

🔥 Practical Example

Remove even numbers:

numbers = [1, 2, 3, 4, 5, 6]

odd_numbers = [n for n in numbers if n % 2 != 0]

print(odd_numbers)

📌 Summary of List Methods

MethodPurpose
append()Add one element
insert()Add at position
extend()Add multiple elements
remove()Remove by value
pop()Remove by index
clear()Remove all
index()Find position
count()Count occurrences
sort()Sort list
reverse()Reverse order
copy()Copy list. 

🐍 Strings in Python

🔹 What is a String?

A string is a sequence of characters enclosed in quotes.

name = "Hari"
message = 'Hello World'

Python supports:

  • Single quotes ' '

  • Double quotes " "

  • Triple quotes """ """ (multi-line)


🔹 Multi-line String

text = """This is
a multi-line
string"""

🔹 String Characteristics

✔ Ordered
✔ Immutable (Cannot change directly)
✔ Supports indexing and slicing


🔹 Indexing

name = "Python"

print(name[0])   # P
print(name[-1])  # n

🔹 Slicing

print(name[0:3])  # Pyt
print(name[:4])   # Pyth
print(name[2:])   # thon

🔥 String is Immutable

text = "Hello"
text[0] = "h"   # ❌ Error

Instead:

text = "Hello"
text = "h" + text[1:]
print(text)

📌 Important String Methods


1️⃣ upper()

Convert to uppercase.

text = "python"
print(text.upper())

Output:

PYTHON

2️⃣ lower()

text = "PYTHON"
print(text.lower())

3️⃣ capitalize()

Capitalizes first letter.

print("python".capitalize())

4️⃣ title()

Capitalizes each word.

print("hello world".title())

5️⃣ strip()

Removes spaces from both sides.

text = "   hello   "
print(text.strip())

lstrip() / rstrip()

text = "   hello   "
print(text.lstrip())
print(text.rstrip())

6️⃣ replace()

Replace substring.

text = "I love Java"
print(text.replace("Java", "Python"))

7️⃣ split()

Split string into list.

text = "apple,banana,mango"
print(text.split(","))

Output:

['apple', 'banana', 'mango']

8️⃣ join()

Join list into string.

words = ["Hello", "World"]
print(" ".join(words))

9️⃣ find()

Returns index or -1.

text = "Python"
print(text.find("th"))

🔟 index()

Same as find but gives error if not found.

print(text.index("th"))

1️⃣1️⃣ count()

Counts occurrences.

print("banana".count("a"))

1️⃣2️⃣ startswith() / endswith()

text = "Python Programming"

print(text.startswith("Python"))
print(text.endswith("ing"))

1️⃣3️⃣ isalpha()

print("Python".isalpha())  # True
print("Python123".isalpha())  # False

1️⃣4️⃣ isdigit()

print("123".isdigit())
print("12a3".isdigit())

1️⃣5️⃣ isalnum()

print("Python123".isalnum())

1️⃣6️⃣ isspace()

print("   ".isspace())

1️⃣7️⃣ format()

name = "Hari"
age = 25

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

🔥 f-Strings (Recommended Modern Way)

name = "Hari"
age = 25

print(f"My name is {name} and I am {age} years old")

🔥 Escape Characters

EscapeMeaning
\nNew line
\tTab
\Backslash
"Double quote

Example:

print("Hello\nWorld")

🔥 String Concatenation

first = "Hari"
last = "Mohan"

print(first + " " + last)

🔥 String Multiplication

print("Hi " * 3)

🔥 Iterating Over String

for char in "Python":
    print(char)

🔥 Practical Example

Check palindrome:

text = "madam"

if text == text[::-1]:
    print("Palindrome")

📌 Summary of Important String Methods

MethodPurpose
upper()Uppercase
lower()Lowercase
capitalize()First letter uppercase
title()Capitalize each word
strip()Remove spaces
replace()Replace text
split()Convert to list
join()Convert list to string
find()Find index
count()Count occurrences
startswith()Check prefix
endswith()Check suffix
isalpha()Letters only
isdigit()Digits only
isalnum()Alphanumeric


🐍 Dictionary in Python

🔹 What is a Dictionary?

A dictionary is:

  • A collection of key–value pairs

  • Ordered (Python 3.7+)

  • Mutable (can change)

  • Keys must be unique

  • Keys must be immutable (string, number, tuple)


🔹 Creating a Dictionary

student = {
    "name": "Hari",
    "age": 25,
    "course": "Python"
}

🔹 Accessing Values

Using Key

print(student["name"])

⚠️ If key doesn’t exist → Error


Using get() (Safe Way)

print(student.get("name"))
print(student.get("marks"))  # None (no error)

🔹 Dictionary is Mutable

student["age"] = 26
print(student)

🔹 Adding New Key

student["city"] = "Bangalore"

🔹 Removing Items


1️⃣ pop()

Removes specific key.

student.pop("age")

2️⃣ popitem()

Removes last inserted item.

student.popitem()

3️⃣ del

del student["course"]

4️⃣ clear()

student.clear()

📌 Important Dictionary Methods


1️⃣ keys()

Returns all keys.

student = {"name": "Hari", "age": 25}

print(student.keys())

Output:

dict_keys(['name', 'age'])

2️⃣ values()

Returns all values.

print(student.values())

3️⃣ items()

Returns key-value pairs as tuples.

print(student.items())

Output:

dict_items([('name', 'Hari'), ('age', 25)])

4️⃣ update()

Updates dictionary with another dictionary.

student = {"name": "Hari", "age": 25}

student.update({"age": 30, "city": "Delhi"})
print(student)

5️⃣ setdefault()

Returns value if key exists, otherwise inserts key with default value.

student = {"name": "Hari"}

student.setdefault("age", 25)
print(student)

6️⃣ copy()

Creates shallow copy.

new_student = student.copy()

🔥 Looping Through Dictionary


Loop Through Keys

for key in student:
    print(key)

Loop Through Values

for value in student.values():
    print(value)

Loop Through Key-Value

for key, value in student.items():
    print(key, value)

🔥 Nested Dictionary

students = {
    "student1": {"name": "Hari", "age": 25},
    "student2": {"name": "Mohan", "age": 22}
}

print(students["student1"]["name"])

🔥 Dictionary Comprehension (Important)

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

Output:

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

🔥 Check if Key Exists

if "name" in student:
    print("Key exists")

🔥 Difference Between dict vs list

FeatureListDictionary
Access byIndexKey
Syntax[]{}
StoresValuesKey-Value pairs

🔥 Practical Example

Count frequency of characters:

text = "banana"
frequency = {}

for char in text:
    frequency[char] = frequency.get(char, 0) + 1

print(frequency)

Output:

{'b': 1, 'a': 3, 'n': 2}

📌 Summary of Important Dictionary Methods

MethodPurpose
get()Safe access
keys()Get all keys
values()Get all values
items()Get key-value pairs
update()Update dictionary
pop()Remove by key
popitem()Remove last item
clear()Remove all
copy()Copy dictionary
setdefault()Insert default value

🐍 Tuple in Python

🔹 What is a Tuple?

A tuple is:

  • An ordered collection of elements

  • Immutable (cannot change after creation)

  • Allows duplicate values

  • Can store different data types


🔹 Creating a Tuple

Basic Tuple

numbers = (1, 2, 3, 4)

Without Parentheses (Tuple Packing)

numbers = 1, 2, 3
print(type(numbers))

Output:

<class 'tuple'>

Single Element Tuple (Important ⚠️)

t = (5,)

Without comma:

t = (5)   # This is int, not tuple

🔹 Accessing Tuple Elements

Indexing

t = (10, 20, 30)

print(t[0])    # 10
print(t[-1])   # 30

Slicing

t = (1, 2, 3, 4, 5)

print(t[1:4])  # (2, 3, 4)

🔥 Tuple is Immutable

t = (1, 2, 3)
t[0] = 100   # ❌ Error

You cannot modify, add, or remove elements.


🔹 Looping Through Tuple

t = (1, 2, 3)

for item in t:
    print(item)

📌 Tuple Methods (Very Few)

Tuple has only 2 built-in methods:


1️⃣ count()

Counts occurrences.

t = (1, 2, 2, 3, 2)
print(t.count(2))

Output:

3

2️⃣ index()

Returns index of first match.

t = (10, 20, 30)
print(t.index(20))

Output:

1

🔥 Tuple Packing and Unpacking (Important)


Packing

person = ("Hari", 25, "Developer")

Unpacking

name, age, profession = person

print(name)
print(age)
print(profession)

Using * (Star Unpacking)

numbers = (1, 2, 3, 4, 5)

a, *b, c = numbers

print(a)  # 1
print(b)  # [2, 3, 4]
print(c)  # 5

🔥 Tuple vs List

FeatureTupleList
Syntax()[]
Mutable❌ No✔ Yes
PerformanceFasterSlower
MemoryLessMore
Methods2Many

🔥 Why Use Tuple?

✔ When data should not change
✔ Faster performance
✔ Can be used as dictionary keys
✔ Safe data storage


🔥 Tuple as Dictionary Key

location = {(10, 20): "Point A"}

print(location[(10, 20)])

Works because tuple is immutable.


🔥 Nested Tuple

t = ((1, 2), (3, 4))

print(t[0][1])  # 2

🔥 Convert Tuple ↔ List

Tuple to List

t = (1, 2, 3)
l = list(t)

List to Tuple

l = [1, 2, 3]
t = tuple(l)

🔥 Practical Example

Swap two variables (Python special feature):

a = 10
b = 20

a, b = b, a

print(a, b)

Behind the scenes → Tuple packing & unpacking.


📌 Summary

  • Tuple = ordered + immutable

  • Uses ()

  • Allows duplicates

  • Faster than list

  • Methods: count(), index()

  • Supports unpacking



🐍 Sets in Python

🔹 What is a Set?

A set is:

  • An unordered collection

  • Contains unique elements (no duplicates)

  • Mutable (can add/remove elements)

  • Written using {}


🔹 Creating a Set

numbers = {1, 2, 3, 4}
print(type(numbers))

🔹 Duplicate Values Removed Automatically

nums = {1, 2, 2, 3, 3, 4}
print(nums)

Output:

{1, 2, 3, 4}

🔹 Empty Set (Important ⚠️)

s = set()   # Correct

❌ Wrong:

s = {}   # This creates dictionary

🔥 Set Characteristics

✔ Unordered
✔ No duplicates
✔ Mutable
✔ Elements must be immutable (int, str, tuple)


🔹 Accessing Elements

Sets do NOT support indexing.

s = {1, 2, 3}
# s[0] ❌ Error

To access elements:

for item in s:
    print(item)

📌 Important Set Methods


1️⃣ add()

Add single element.

s = {1, 2}
s.add(3)

print(s)

2️⃣ update()

Add multiple elements.

s = {1, 2}
s.update([3, 4, 5])

print(s)

3️⃣ remove()

Removes element (Error if not found).

s = {1, 2, 3}
s.remove(2)

4️⃣ discard()

Removes element (No error if not found).

s = {1, 2, 3}
s.discard(5)   # No error

5️⃣ pop()

Removes random element (because unordered).

s = {10, 20, 30}
s.pop()
print(s)

6️⃣ clear()

Remove all elements.

s = {1, 2, 3}
s.clear()
print(s)

7️⃣ copy()

Create shallow copy.

s1 = {1, 2, 3}
s2 = s1.copy()

🔥 Set Operations (Very Important)

Sets are powerful for mathematical operations.


1️⃣ Union

Combines two sets.

a = {1, 2, 3}
b = {3, 4, 5}

print(a.union(b))

Or:

print(a | b)

Output:

{1, 2, 3, 4, 5}

2️⃣ Intersection

Common elements.

print(a.intersection(b))

Or:

print(a & b)

Output:

{3}

3️⃣ Difference

Elements in A but not in B.

print(a.difference(b))

Or:

print(a - b)

4️⃣ Symmetric Difference

Elements not common in both.

print(a.symmetric_difference(b))

Or:

print(a ^ b)

🔥 Subset and Superset


issubset()

a = {1, 2}
b = {1, 2, 3}

print(a.issubset(b))

issuperset()

print(b.issuperset(a))

🔥 Frozen Set (Immutable Set)

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

Cannot modify.

Used when:

  • Want immutable set

  • Use as dictionary key


🔥 Practical Examples


Remove Duplicates from List

numbers = [1, 2, 2, 3, 4, 4]
unique = set(numbers)

print(unique)

Find Common Elements Between Lists

a = [1, 2, 3]
b = [2, 3, 4]

common = set(a) & set(b)
print(common)

🔥 Set Comprehension

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

🔥 Membership Testing (Very Fast)

s = {1, 2, 3}

if 2 in s:
    print("Found")

Set lookup is O(1) average time complexity.


📌 Summary of Important Set Methods

MethodPurpose
add()Add element
update()Add multiple
remove()Remove (error if missing)
discard()Remove (no error)
pop()Remove random
clear()Remove all
union()Combine sets
intersection()Common elements
difference()Subtract sets
symmetric_difference()Non-common elements
issubset()Check subset
issuperset()Check superset
copy()Copy set

🔥 Set vs List vs Tuple

FeatureSetListTuple
Ordered❌ No✔ Yes✔ Yes
Duplicates❌ No✔ Yes✔ Yes
Mutable✔ Yes✔ Yes❌ No
Indexing❌ No✔ Yes✔ Yes

🐍 Section 4: Functions in Python

🔹 What is a Function?

A function is a reusable block of code that performs a specific task.

Why use functions?

✔ Avoid repetition
✔ Make code modular
✔ Improve readability
✔ Easy testing


1️⃣ Defining a Function

🔹 Basic Syntax

def function_name(parameters):
    # code block
    return value

🔹 Simple Example

def greet():
    print("Hello, Hari!")

greet()

Output:

Hello, Hari!

🔹 Function with Parameters

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

greet("Hari")

🔹 Function with Return Value

def add(a, b):
    return a + b

result = add(10, 20)
print(result)

2️⃣ Default Parameters

Allows parameters to have default values.


🔹 Example

def greet(name="Guest"):
    print(f"Hello, {name}!")

greet()         # Uses default
greet("Hari")   # Overrides default

⚠️ Rule: Default parameters must come after non-default parameters.

❌ Wrong:

def test(a=5, b):
    pass

✔ Correct:

def test(a, b=5):
    pass

3️⃣ Keyword Arguments

Instead of passing arguments by position, pass by name.


🔹 Positional Arguments

def student(name, age):
    print(name, age)

student("Hari", 25)

🔹 Keyword Arguments

student(age=25, name="Hari")

Order does not matter.


🔹 Mixing Positional + Keyword

student("Hari", age=25)

But positional must come first.


4️⃣ Recursive Functions

A function that calls itself.


🔹 Example: Factorial

Mathematical:

5! = 5 × 4 × 3 × 2 × 1

Recursive Implementation

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))

🔹 Important Concepts in Recursion

✔ Base case (Stopping condition)
✔ Recursive case (Function calls itself)

Without base case → Infinite recursion.


🔹 Example: Fibonacci

def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(6))

5️⃣ Lambda Expressions (Anonymous Functions)

Used to create small functions in one line.


🔹 Syntax

lambda parameters: expression

🔹 Example

Normal function:

def square(x):
    return x * x

Lambda version:

square = lambda x: x * x
print(square(5))

🔹 Lambda with Multiple Parameters

add = lambda a, b: a + b
print(add(10, 20))

🔹 Lambda with sorted()

students = [("Hari", 25), ("Mohan", 22)]

students.sort(key=lambda x: x[1])
print(students)

6️⃣ Docstrings

Used to document functions.


🔹 Basic Docstring

def add(a, b):
    """
    This function returns the sum of two numbers.
    """
    return a + b

🔹 Access Docstring

print(add.__doc__)

🔹 Best Practice Docstring Format

def multiply(a, b):
    """
    Multiply two numbers.

    Parameters:
        a (int): First number
        b (int): Second number

    Returns:
        int: Product of a and b
    """
    return a * b

🔥 Advanced Concepts (Interview Important)


🔹 *args (Variable Arguments)

def add(*numbers):
    return sum(numbers)

print(add(1, 2, 3, 4))

🔹 **kwargs (Keyword Arguments)

def student(**details):
    print(details)

student(name="Hari", age=25)

🔹 Return Multiple Values

def calculate(a, b):
    return a + b, a - b

sum_val, diff_val = calculate(10, 5)
print(sum_val, diff_val)

Python returns tuple internally.


🔥 Function Scope

Local vs Global

x = 10

def test():
    x = 5
    print(x)

test()
print(x)

📌 Summary

ConceptPurpose
defDefine function
returnSend value back
Default parameterPredefined value
Keyword argumentsNamed arguments
RecursionFunction calls itself
LambdaOne-line anonymous function
DocstringFunction documentation
*argsVariable positional arguments
**kwargsVariable keyword arguments



🐍 Section 8: Exception Handling in Python

🔹 What is an Exception?

An exception is an error that occurs during program execution.

Example:

print(10 / 0)

Output:

ZeroDivisionError: division by zero

Without handling → program crashes ❌
With handling → program continues gracefully ✔


🔥 Why Exception Handling is Important?

✔ Prevent program crash
✔ Show user-friendly message
✔ Control program flow
✔ Handle unexpected inputs


1️⃣ try…except

Used to handle errors gracefully.


🔹 Basic Syntax

try:
    # risky code
except:
    # error handling code

🔹 Example

try:
    num = int(input("Enter a number: "))
    print(10 / num)
except:
    print("Invalid input or division by zero!")

Now program doesn’t crash.


🔹 Handling Specific Exceptions (Best Practice)

try:
    num = int(input("Enter a number: "))
    print(10 / num)
except ZeroDivisionError:
    print("Cannot divide by zero!")
except ValueError:
    print("Please enter a valid number!")

✔ Always catch specific exceptions.


🔹 Multiple Exceptions in One Line

try:
    x = int("abc")
except (ValueError, TypeError):
    print("Error occurred")

🔹 Exception Object

try:
    x = 10 / 0
except ZeroDivisionError as e:
    print("Error:", e)

2️⃣ try…except…finally

finally block runs no matter what happens.


🔹 Syntax

try:
    # risky code
except:
    # error handling
finally:
    # always executes

🔹 Example

try:
    file = open("test.txt")
    print(file.read())
except FileNotFoundError:
    print("File not found")
finally:
    print("Execution completed")

Even if error happens → finally runs.


🔹 Real-world Example (Important)

try:
    file = open("data.txt", "r")
    data = file.read()
except FileNotFoundError:
    print("File missing")
finally:
    print("Closing program")

Usually used for:

  • Closing files

  • Releasing resources

  • Database connections


3️⃣ try…except…else

else runs only if NO exception occurs.


🔹 Syntax

try:
    # risky code
except:
    # error handling
else:
    # runs if no error

🔹 Example

try:
    num = int(input("Enter number: "))
except ValueError:
    print("Invalid input")
else:
    print("You entered:", num)

If input is valid → else runs
If error → except runs


🔥 Complete Structure (All Together)

try:
    num = int(input("Enter number: "))
    result = 10 / num
except ZeroDivisionError:
    print("Cannot divide by zero")
except ValueError:
    print("Invalid input")
else:
    print("Result:", result)
finally:
    print("Done")

🔥 Execution Flow

  1. try block runs first

  2. If error → matching except runs

  3. If no error → else runs

  4. finally always runs


🔥 Raising Exceptions Manually

You can create custom errors using raise.

age = -5

if age < 0:
    raise ValueError("Age cannot be negative")

🔥 Custom Exception

class MyError(Exception):
    pass

try:
    raise MyError("Custom error occurred")
except MyError as e:
    print(e)

🔥 Common Built-in Exceptions

ExceptionCause
ZeroDivisionErrorDivision by zero
ValueErrorWrong value type
TypeErrorWrong type operation
IndexErrorInvalid index
KeyErrorMissing dictionary key
FileNotFoundErrorFile not found

🔥 Practical Example

Safe dictionary access:

data = {"name": "Hari"}

try:
    print(data["age"])
except KeyError:
    print("Key not found")

🔥 Interview-Level Concept

Why use else?

Cleaner structure:

try:
    value = int("10")
except ValueError:
    print("Error")
else:
    print(value * 2)

Avoids placing normal logic inside try.


📌 Summary

BlockWhen It Runs
tryAlways first
exceptIf exception occurs
elseIf no exception
finallyAlways runs

Working with Files in Python

Python provides built-in functions and modules to work with files.


🔹 Opening a File

Basic syntax:

file = open("filename.txt", "mode")

Better way (recommended):

with open("filename.txt", "mode") as file:
    # work with file

✔ with automatically closes file
✔ Prevents memory leaks


📌 File Modes

ModeDescription
"r"Read (default)
"w"Write (overwrites file)
"a"Append
"x"Create new file
"rb"Read binary
"wb"Write binary

1️⃣ Read from a Text File


🔹 Read Entire File

with open("sample.txt", "r") as file:
    content = file.read()
    print(content)

🔹 Read Line by Line

with open("sample.txt", "r") as file:
    for line in file:
        print(line.strip())

🔹 Read One Line

with open("sample.txt", "r") as file:
    print(file.readline())

🔹 Read All Lines as List

with open("sample.txt", "r") as file:
    lines = file.readlines()
    print(lines)

2️⃣ Write to a Text File

⚠️ "w" overwrites file if it exists.

with open("sample.txt", "w") as file:
    file.write("Hello, Hari!\n")
    file.write("Welcome to Python.")

3️⃣ Append to a File

with open("sample.txt", "a") as file:
    file.write("\nNew line added.")

4️⃣ Create a New Text File

Using "x" mode:

with open("newfile.txt", "x") as file:
    file.write("This is a new file.")

⚠️ If file exists → error


5️⃣ Check if a File Exists

Using os module:

import os

if os.path.exists("sample.txt"):
    print("File exists")
else:
    print("File does not exist")

6️⃣ Read CSV Files

Python provides built-in csv module.


🔹 Example CSV File (data.csv)

name,age,city
Hari,25,Bangalore
Mohan,22,Delhi

🔹 Read CSV File

import csv

with open("data.csv", "r") as file:
    reader = csv.reader(file)
    
    for row in reader:
        print(row)

Output:

['name', 'age', 'city']
['Hari', '25', 'Bangalore']
['Mohan', '22', 'Delhi']

🔹 Read CSV as Dictionary

import csv

with open("data.csv", "r") as file:
    reader = csv.DictReader(file)
    
    for row in reader:
        print(row["name"], row["age"])

7️⃣ Write CSV Files


🔹 Writing Basic CSV

import csv

data = [
    ["name", "age", "city"],
    ["Hari", 25, "Bangalore"],
    ["Mohan", 22, "Delhi"]
]

with open("output.csv", "w", newline="") as file:
    writer = csv.writer(file)
    writer.writerows(data)

🔹 Writing CSV with Dictionary

import csv

data = [
    {"name": "Hari", "age": 25},
    {"name": "Mohan", "age": 22}
]

with open("output.csv", "w", newline="") as file:
    fieldnames = ["name", "age"]
    
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(data)

8️⃣ Rename a File

Using os.rename():

import os

os.rename("oldname.txt", "newname.txt")

9️⃣ Delete a File

Using os.remove():

import os

os.remove("sample.txt")

🔥 Handling File Exceptions (Important)

import os

try:
    with open("sample.txt", "r") as file:
        print(file.read())
except FileNotFoundError:
    print("File not found")

🔥 Real-World Example

Append user input to file:

name = input("Enter your name: ")

with open("users.txt", "a") as file:
    file.write(name + "\n")

🔥 Best Practices

✔ Always use with statement
✔ Handle exceptions
✔ Close files properly
✔ Use newline="" when writing CSV
✔ Validate file existence


📌 Summary

OperationMethod
Read fileopen(..., "r")
Write fileopen(..., "w")
Append fileopen(..., "a")
Create fileopen(..., "x")
Check existsos.path.exists()
Read CSVcsv.reader()
Write CSVcsv.writer()
Rename fileos.rename()
Delete fileos.remove()


import collections
print(dir(collections))

…and grabbed a subset. Let’s go through everything in that list, but in a sane way:

  • First: things you actually use (public API)

  • Then: internal / helper stuff (you normally ignore)

✅ Public, Useful Things

1. ChainMap

What it is: A view over multiple dicts as if they were one.

  • Lookup: checks first dict, then second, etc.

  • Useful for: layered configs, scopes, overriding defaults.

from collections import ChainMap

defaults = {"timeout": 30, "retries": 3}
env = {"timeout": 10}

config = ChainMap(env, defaults)
print(config["timeout"])  # 10 (from env)
print(config["retries"])  # 3  (from defaults)

2. Counter

What it is: A dict subclass for counting hashable items.

  • Key = item, Value = count.

  • Great for frequency problems (DSA, interviews).

from collections import Counter

c = Counter("banana")
print(c)          # Counter({'a': 3, 'n': 2, 'b': 1})
print(c["a"])     # 3
print(c.most_common(1))  # [('a', 3)]

3. OrderedDict

What it is: A dict that remembers insertion order.

  • Before Python 3.7, normal dict didn’t guarantee order.

  • Still useful for some advanced patterns (like LRU Cache).

from collections import OrderedDict

od = OrderedDict()
od["a"] = 1
od["b"] = 2
print(od)  # OrderedDict([('a', 1), ('b', 2)])

4. UserDict

What it is: A wrapper around dict for easy subclassing.

  • Instead of subclassing dict directly, you subclass UserDict.

  • Safer / more predictable inheritance.

from collections import UserDict

class MyDict(UserDict):
    def __setitem__(self, key, value):
        print("Setting:", key, value)
        super().__setitem__(key, value)

d = MyDict()
d["x"] = 10

5. UserList

What it is: Same idea as UserDict but for list.

from collections import UserList

class MyList(UserList):
    def append(self, item):
        print("Appending:", item)
        super().append(item)

l = MyList()
l.append(5)

6. UserString

What it is: Wrapper around string for easy subclassing.

from collections import UserString

class MyString(UserString):
    def shout(self):
        return self.data.upper() + "!!!"

s = MyString("hello")
print(s.shout())  # HELLO!!!

7. defaultdict

What it is: Dict with default value factory.

  • Avoids KeyError by creating default values.

from collections import defaultdict

d = defaultdict(int)
d["a"] += 1
print(d)  # defaultdict(<class 'int'>, {'a': 1})

8. deque

What it is: Double-ended queue.

  • Fast append / appendleft / pop / popleft.

  • Used a LOT in DSA (BFS, sliding window).

from collections import deque

dq = deque([1, 2, 3])
dq.appendleft(0)
dq.append(4)
print(dq)  # deque([0, 1, 2, 3, 4])

9. namedtuple

What it is: Factory to create tuple subclasses with named fields.

  • Lightweight “class-like” structure.

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(2, 3)
print(p.x, p.y)   # 2 3

10. abc

What it is: The abc module (abstract base classes) re-exported.

  • Contains abstract container types (Mapping, Sequence, etc.).

  • Used for isinstance checks and custom container types.

from collections.abc import Mapping, Sequence

⚙️ Internal / Implementation Details (You Normally Don’t Use)

These things exist for the implementation of collections itself, or for internal mechanics. You rarely (almost never) need them directly.

I’ll keep them short:


11. _Link

Internal helper class/structure used by OrderedDict to maintain its doubly-linked list of entries.


12–14. _OrderedDictItemsView, _OrderedDictKeysView, _OrderedDictValuesView

Specialized view classes used when you call:

od.keys()
od.items()
od.values()

on an OrderedDict. They track order + changes efficiently.


15. __all__

List of public names that from collections import * will import.


16. __builtins__

Reference to the built-in namespace (implementation detail).


17. __cached__

Path to the cached compiled file (.pyc), used by Python’s import system.


18. __doc__

Module docstring for collections.


19. __file__

Path to the source file of the collections module on your system.


20. __loader__

Object that loaded the module (part of Python import internals).


21. __name__

The name of the module, "collections".


22. __package__

Package name for relative imports. For collections, usually "collections".


23. __path__

Only relevant when module is a package; for collections, may be used to locate submodules.


24. __spec__

Import specification object that describes how this module was loaded.


25. _chain

Internal helper used by ChainMap or other utilities to chain iterables.


26. _collections_abc

Internal module that actually defines the abstract base classes for containers (Mapping, MutableMapping, etc.), then collections.abc exposes them.


27. _count_elements

Internal helper function used by Counter to efficiently count elements (implemented in C for speed in CPython).


28. _deque_iterator

Internal iterator type used for iterating over deque efficiently.


29. _eq

Internal equality helper used by some of the collections’ classes.


30. _iskeyword

Internal helper checking if a string is a Python keyword (used by namedtuple to avoid illegal field names).


31. _itemgetter

Internal helper related to operator.itemgetter, used in namedtuple or views.


32. _proxy

Internal wrapper / proxy object used by some collections internals (often for type-safe behavior).


33. _recursive_repr

Internal decorator to avoid infinite recursion in __repr__ for self-referential structures.


34. _repeat

Internal helper similar to itertools.repeat, for performance.


35. _starmap

Internal helper similar to itertools.starmap, used in named tuple or other internal operations.


36. _sys

Reference to the sys module used internally in collections.


37. _tuplegetter

Internal helper used by namedtuple to generate efficient getters for tuple fields.


🧠 Big Picture

From your list, the ones you really care about in real code & interviews:

  • Counter

  • defaultdict

  • deque

  • namedtuple

  • OrderedDict

  • ChainMap

  • UserDict / UserList / UserString (when subclassing)

Everything starting with _ or __ is mainly internal or metadata.


Perfect 👌
Let’s go deep into OOP (Object-Oriented Programming) in Python — beginner to advanced, with clean explanations and examples.


🐍 OOP in Python (Object-Oriented Programming)

🔹 What is OOP?

OOP is a programming paradigm based on:

  • Objects

  • Classes

  • Encapsulation

  • Abstraction

  • Inheritance

  • Polymorphism


🧱 1️⃣ Class and Object

🔹 Class

A class is a blueprint for creating objects.

class Person:
    pass

🔹 Object

An object is an instance of a class.

p = Person()
print(p)

🧠 2️⃣ Constructor (__init__)

Used to initialize object data.

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

p1 = Person("Hari", 25)
print(p1.name)

🔹 What is self?

  • Refers to current object

  • Required in instance methods

  • Automatically passed by Python


🔹 3️⃣ Instance Methods

class Person:
    def __init__(self, name):
        self.name = name
    
    def greet(self):
        print(f"Hello, I am {self.name}")

p = Person("Hari")
p.greet()

🔐 4️⃣ Encapsulation

Hiding internal details.


🔹 Public Variable

self.name

🔹 Protected (Convention)

self._salary

🔹 Private (Name Mangling)

self.__password

Example:

class Bank:
    def __init__(self):
        self.__balance = 1000
    
    def get_balance(self):
        return self.__balance

b = Bank()
print(b.get_balance())

🎭 5️⃣ Inheritance

Allows one class to inherit another.


🔹 Example

class Animal:
    def speak(self):
        print("Animal speaks")

class Dog(Animal):
    def bark(self):
        print("Dog barks")

d = Dog()
d.speak()
d.bark()

🔁 6️⃣ Method Overriding

Child class overrides parent method.

class Animal:
    def speak(self):
        print("Animal sound")

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

d = Dog()
d.speak()

🎭 7️⃣ Polymorphism

Same method name, different behavior.

class Cat:
    def speak(self):
        print("Meow")

class Dog:
    def speak(self):
        print("Bark")

for animal in (Cat(), Dog()):
    animal.speak()

🧩 8️⃣ Abstraction

Hide implementation details.

Use abc module.

from abc import ABC, abstractmethod

class Shape(ABC):
    
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def area(self):
        return 3.14 * 5 * 5

🔄 9️⃣ Class Variables vs Instance Variables

class Car:
    wheels = 4   # class variable
    
    def __init__(self, brand):
        self.brand = brand  # instance variable

c1 = Car("BMW")
print(c1.wheels)

🔹 🔟 Static Method

Does not access instance or class.

class Math:
    
    @staticmethod
    def add(a, b):
        return a + b

print(Math.add(5, 3))

🔹 1️⃣1️⃣ Class Method

Accesses class variable.

class Car:
    wheels = 4
    
    @classmethod
    def get_wheels(cls):
        return cls.wheels

print(Car.get_wheels())

🔁 1️⃣2️⃣ Multiple Inheritance

class A:
    pass

class B:
    pass

class C(A, B):
    pass

🧠 MRO (Method Resolution Order)

Python decides method lookup order using MRO.

print(C.__mro__)

🔥 1️⃣3️⃣ Magic Methods (Dunder Methods)

Special methods starting with __.

Examples:

  • __init__

  • __str__

  • __repr__

  • __len__

  • __add__


Example

class Person:
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return f"Person: {self.name}"

p = Person("Hari")
print(p)

📌 OOP Pillars Summary

ConceptMeaning
EncapsulationHide data
AbstractionHide implementation
InheritanceReuse code
PolymorphismSame interface, different behavior

🎯 Interview-Important Topics

If preparing seriously:

✔ Encapsulation
✔ Inheritance
✔ Polymorphism
✔ Method overriding
✔ Static vs Class method
✔ MRO
✔ Magic methods
✔ Abstract classes

Perfect 👌
Let’s go deep into Python Concurrency — structured, practical, and interview-ready.


🐍 Python Concurrency

🔹 What is Concurrency?

Concurrency means:

Running multiple tasks in overlapping time.

Example:

  • Handling multiple API requests

  • Downloading multiple files

  • Processing background jobs


🔥 Concurrency vs Parallelism

TermMeaning
ConcurrencyTasks overlap in execution
ParallelismTasks run at the same time (multiple CPU cores)

🧠 Why Concurrency in Python is Special?

Because of:

🚫 GIL (Global Interpreter Lock)

  • Only one thread executes Python bytecode at a time

  • Affects CPU-bound tasks

  • Does NOT block I/O-bound concurrency


🎯 Types of Concurrency in Python

  1. Threading (I/O-bound tasks)

  2. Multiprocessing (CPU-bound tasks)

  3. Asyncio (Async/Await) (High-performance I/O)


1️⃣ Threading

Best for:

  • API calls

  • File I/O

  • Network requests


🔹 Basic Example

import threading
import time

def task():
    print("Task started")
    time.sleep(2)
    print("Task completed")

t1 = threading.Thread(target=task)
t2 = threading.Thread(target=task)

t1.start()
t2.start()

t1.join()
t2.join()

🔹 When to Use Threading

✔ I/O-bound tasks
✔ Waiting for network response
✔ Multiple small tasks

❌ Not good for CPU-heavy computation (due to GIL)


2️⃣ Multiprocessing

Best for:

  • CPU-intensive tasks

  • ML training

  • Heavy computation

Each process:

  • Has its own memory

  • Has its own Python interpreter

  • No GIL blocking


🔹 Example

from multiprocessing import Process
import time

def task():
    print("Processing...")
    time.sleep(2)

p1 = Process(target=task)
p2 = Process(target=task)

p1.start()
p2.start()

p1.join()
p2.join()

🔹 When to Use Multiprocessing

✔ CPU-bound workloads
✔ Data processing
✔ Parallel model inference


3️⃣ Asyncio (Modern Approach)

Best for:

  • High-performance web servers

  • APIs

  • Chat systems

  • Real-time apps


🔹 Example

import asyncio

async def task():
    print("Start")
    await asyncio.sleep(2)
    print("End")

async def main():
    await asyncio.gather(task(), task())

asyncio.run(main())

🔹 Why Async is Powerful?

  • Single-threaded

  • Event loop-based

  • Non-blocking I/O

  • Highly scalable

Used in:

  • FastAPI

  • Async web scraping

  • High-concurrency servers


🔥 Thread vs Process vs Async

FeatureThreadingMultiprocessingAsyncio
GIL affectedYesNoYes (but non-blocking)
Best forI/OCPUHigh I/O
MemorySharedSeparateShared
SpeedMediumHigh (CPU)High (I/O)

🧠 Real-world Example

Web Server (FastAPI)

FastAPI uses:

  • Async functions

  • Event loop

  • Non-blocking requests

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def home():
    return {"message": "Hello"}

🔥 ThreadPoolExecutor

Easier way to manage threads:

from concurrent.futures import ThreadPoolExecutor

def square(x):
    return x * x

with ThreadPoolExecutor() as executor:
    results = executor.map(square, [1, 2, 3, 4])

print(list(results))

🔥 ProcessPoolExecutor

from concurrent.futures import ProcessPoolExecutor

with ProcessPoolExecutor() as executor:
    results = executor.map(square, [1, 2, 3, 4])

print(list(results))

⚠️ Common Concurrency Problems

  1. Race condition

  2. Deadlock

  3. Shared state mutation

  4. Blocking calls inside async


🔐 Thread Safety (Lock Example)

import threading

lock = threading.Lock()

def safe_task():
    with lock:
        print("Safe access")

🧠 Interview-Level Points

If preparing seriously:

✔ Understand GIL clearly
✔ Know when to use threads vs processes
✔ Understand async event loop
✔ Know I/O vs CPU-bound difference
✔ Thread safety basics
✔ Deadlock explanation


🚀 When to Use What (Practical Guide)

🧮 CPU Heavy Task?

→ Use multiprocessing

🌐 API Calls / DB Queries?

→ Use async

🗂 File Handling?

→ Threading or async


🔥 Example: High-Performance LLM API

If building LLM backend:

  • Async FastAPI

  • Redis caching

  • Background tasks

  • Multiprocessing for embedding

  • Streaming responses


🎯 Summary

Python concurrency options:

  1. Threading → I/O tasks

  2. Multiprocessing → CPU tasks

  3. Asyncio → Scalable I/O



🧩 Advanced Python: Imports & Modules

Python’s import system is the backbone of how you organize and reuse code.
Understanding it deeply makes you way stronger as a Python dev.


1. What is a Module?

A module is just a .py file that contains Python code (functions, classes, variables).

Example:

math_utils.py:

# math_utils.py

PI = 3.14159

def add(a, b):
    return a + b

def area_of_circle(r):
    return PI * r * r

Using this module:

# main.py

import math_utils

print(math_utils.PI)
print(math_utils.add(2, 3))
print(math_utils.area_of_circle(5))

Key idea:

import math_utils gives you the module object, and you access its contents via math_utils.something.


2. Different Ways to Import

2.1 import module

import math
print(math.sqrt(16))

✅ Clear
✅ Namespaced
❌ Slightly more typing


2.2 import module as alias

import numpy as np
import math_utils as mu

print(mu.add(1, 2))

Good when:

  • Module name is long

  • You want conventional alias (np, pd, plt)


2.3 from module import name

from math_utils import add, PI

print(add(2, 3))
print(PI)

✅ Less typing
⚠️ But it can pollute the namespace if you import too many names.


2.4 from module import * (Wildcard)

from math_utils import *
print(add(2, 3))

⚠️ Not recommended in real projects because:

  • It hides where things come from

  • Can overwrite names silently

  • Makes code harder to debug

Use only in:

  • Quick REPL / experiments

  • Some educational/demo situations


3. Packages and __init__.py

A package is a folder with an __init__.py file.

Example project:

my_project/
    main.py
    utils/
        __init__.py
        math_utils.py
        string_utils.py

math_utils.py:

def add(a, b):
    return a + b

string_utils.py:

def shout(text):
    return text.upper() + "!"

utils/__init__.py:

from .math_utils import add
from .string_utils import shout

__all__ = ["add", "shout"]

Now in main.py:

from utils import add, shout

print(add(2, 3))
print(shout("hello"))

Here:

  • utils is a package

  • __init__.py controls what from utils import ... exposes


4. Absolute vs Relative Imports

4.1 Absolute Import

From project root:

from utils.math_utils import add

This is unambiguous and recommended for bigger projects.


4.2 Relative Import (inside a package)

Inside utils/string_utils.py:

from .math_utils import add     # from same package
from ..config import settings   # from parent package (if exists)
  • . → same directory

  • .. → parent directory

Relative imports only work inside packages, not from top-level scripts run directly.


5. if __name__ == "__main__":

Every module has a built-in variable __name__.

  • If you import the module → __name__ == "module_name"

  • If you run the module directly → __name__ == "__main__"

Pattern:

# math_utils.py

def add(a, b):
    return a + b

if __name__ == "__main__":
    # this runs only when we execute: python math_utils.py
    print(add(2, 3))

This is great for:

  • Quick tests

  • Demo code

  • CLI entry points


6. How Import Actually Works (Mental Model)

When you do:

import math_utils

Python roughly does:

  1. Look for math_utils in sys.modules (cache)

  2. If not found:

    • Search in directories listed in sys.path

    • Load and execute math_utils.py

    • Store the module object in sys.modules["math_utils"]

  3. Bind the name math_utils in your current file to that module object.

So imports are cached — second import is basically free.

You can inspect:

import sys, math_utils

print(sys.modules.keys())          # all loaded modules
print(math_utils.__file__)         # file path
print(dir(math_utils))             # module attributes

7. Circular Imports (Common Pitfall)

Two modules import each other:

a.py
b.py

a.py:

from b import func_b

def func_a():
    func_b()

b.py:

from a import func_a

def func_b():
    func_a()

This can lead to:

  • ImportError

  • AttributeError (module loaded but object not defined yet)

Fix strategies:

  • Move shared code into a third module

  • Use local imports (import inside function) to delay import

  • Redesign modules to be less tightly coupled

Example of local import:

# b.py
def func_b():
    from a import func_a
    func_a()

8. Realistic Example: Project Layout with Imports

my_app/
    main.py
    app/
        __init__.py
        models.py
        services.py
        config.py

app/config.py:

DB_URL = "postgresql://..."

app/models.py:

from .config import DB_URL

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

app/services.py:

from .models import User

def create_user(name: str):
    return User(name)

main.py:

from app.services import create_user

if __name__ == "__main__":
    user = create_user("Hari")
    print(user.name)

This is close to how real apps are structured.


9. Import Best Practices (for your “Advanced Python” course)

  • ✅ Prefer absolute imports in larger projects

  • ✅ Use from module import name for a few specific functions, not everything

  • ❌ Avoid from module import * in production

  • ✅ Keep import section grouped and ordered:

    1. Standard library

    2. Third-party

    3. Local imports

Example style:

# standard lib
import os
import sys
from pathlib import Path

# third-party
import requests
from pydantic import BaseModel

# local
from app.services import create_user

10. Tiny Practice Snippet (Good for your tutorial)

Task:
Create a small package and import from it.

playground/
    main.py
    utils/
        __init__.py
        math_utils.py

utils/math_utils.py:

PI = 3.14159

def circle_area(r: float) -> float:
    return PI * r * r

utils/__init__.py:

from .math_utils import circle_area, PI

__all__ = ["circle_area", "PI"]

main.py:

from utils import circle_area, PI

if __name__ == "__main__":
    print("PI:", PI)
    print("Area of radius 5:", circle_area(5))

Run:

python main.py




Comments

Popular posts from this blog

Machine learning tutorial

RAG (Retrieval-Augmented Generation) tutorial