# Python Learning

## [Table of contents](https://accidental-reason-346.notion.site/Python-Learning-Roadmap-1a846c569e5c8035b1b0e0252f87d62d)

## <mark style="color:red;background-color:yellow;">1. Python Fundamentals (≈10 Hours)</mark>

## <mark style="color:purple;">Environment Setup</mark>

Welcome to the first step of your Python journey! In this section, we'll cover everything you need to get started: installing Python, choosing the right IDE, and running your very first script. This foundation will ensure you have a smooth and efficient environment to support your learning.

Check out this detailed blog post: [**Real Python’s Guide to Installing Python**](https://realpython.com/installing-python/)

<details>

<summary>Environment Setup</summary>

#### 1. Installing Python

Python is open-source and free to use. Here’s how to get it on your machine:

* **Download the Installer:**\
  Visit [python.org](https://www.python.org/downloads/) and download the installer for your operating system. For most users, the latest stable version is the best choice.
* **Run the Installer:**\
  During installation, make sure to check the box that says "Add Python to PATH." This small step allows you to run Python from any command prompt or terminal window without additional configuration.
* **Verify Your Installation:**\
  Open your terminal or command prompt and type:

  ```bash
  python --version
  ```

  You should see the Python version number displayed, confirming the successful installation.

#### 2. Choosing an IDE

An Integrated Development Environment (IDE) simplifies coding by providing features like syntax highlighting, debugging tools, and code suggestions. Here are a few popular options:

* **Visual Studio Code (VS Code):**\
  A versatile and lightweight editor, VS Code is highly recommended for beginners. Its extensive marketplace of extensions, including Python-specific plugins, makes it a powerful tool for development.
* **PyCharm:**\
  PyCharm is a dedicated Python IDE that offers a rich set of features tailored to Python development. While the Community Edition is free, it provides robust tools for coding, testing, and debugging.
* **Other Options:**\
  Alternatives like Atom, Sublime Text, or even Jupyter Notebook (great for data analysis) can also be considered, depending on your project needs.

Choose the one that best fits your style and workflow. If you’re unsure, starting with VS Code is a safe bet due to its balance of simplicity and functionality.

#### 3. Running Your First Python Script

Now that Python is installed and your IDE is set up, it’s time to write and run a simple script.

* **Create a New File:**\
  Open your IDE and create a new file named `hello.py`.
* **Write Your Code:**\
  Type the following code:

  ```python
  print("Hello, Python!")
  ```
* **Run the Script:**\
  There are a couple of ways to execute your script:
  * **Using the Terminal:**\
    Navigate to the directory where your script is saved and run:

    ```bash
    python hello.py
    ```
  * **From the IDE:**\
    Most IDEs provide a “Run” button or similar functionality that allows you to execute the script directly from the interface.

When you run your script, you should see the output “Hello, Python!” in your terminal or the IDE’s console.

</details>

***

## <mark style="color:purple;">Python Basics: Syntax & Data Types</mark>

Understanding Python’s basic syntax and data types is the foundation of writing efficient code. In this section, we will cover variables, numbers, strings, booleans, and operators.

***

### 1. **Variables & Assignment**

A **variable** stores data in memory and can be assigned using `=`.

```python
name = "Alice"   # String
age = 25         # Integer
pi = 3.14        # Float
is_active = True # Boolean
```

* Python is **dynamically typed**, meaning you don’t need to specify the type explicitly.
* Variable names should be meaningful and follow the `snake_case` convention (`my_variable`).

<mark style="color:green;">**Practice Questions**</mark>

1. Declare three variables: an integer, a floating-point number, and a string. Print their values.
2. Swap the values of two variables without using a third variable.
3. What will be the output of the following?

   ```python
   x = 10
   y = "10"
   print(x + y)
   ```

   (Predict the output before running the code!)

***

### 2. **Data Types**

Python has built-in types for handling different kinds of data:

| Type      | Example            | Description                    |
| --------- | ------------------ | ------------------------------ |
| **int**   | `10`               | Whole numbers                  |
| **float** | `3.14`             | Decimal numbers                |
| **str**   | `"Hello"`          | Text enclosed in quotes        |
| **bool**  | `True`, `False`    | Logical values                 |
| **list**  | `[1, 2, 3]`        | Ordered collection (mutable)   |
| **tuple** | `(1, 2, 3)`        | Ordered collection (immutable) |
| **dict**  | `{"key": "value"}` | Key-value pairs                |
| **set**   | `{1, 2, 3}`        | Unordered unique elements      |

Use the `type()` function to check the data type:

```python
print(type(10))       # Output: <class 'int'>
print(type(3.14))     # Output: <class 'float'>
print(type("Hello"))  # Output: <class 'str'>
```

<mark style="color:green;">**Practice Questions**</mark>

1. What is the difference between a list and a tuple? Create one of each.
2. Convert the string `"123"` to an integer and add 10 to it.
3. What will be the output of `bool("")`, `bool(0)`, and `bool([])`? Why?

***

### 3. **Operators in Python**

Python supports several operators:

#### **A. Arithmetic Operators**

| Operator | Description    | Example        |
| -------- | -------------- | -------------- |
| `+`      | Addition       | `5 + 3 = 8`    |
| `-`      | Subtraction    | `10 - 2 = 8`   |
| `*`      | Multiplication | `4 * 2 = 8`    |
| `/`      | Division       | `10 / 2 = 5.0` |
| `//`     | Floor Division | `10 // 3 = 3`  |
| `%`      | Modulus        | `10 % 3 = 1`   |
| `**`     | Exponentiation | `2 ** 3 = 8`   |

#### **B. Comparison Operators**

| Operator | Description              | Example         |
| -------- | ------------------------ | --------------- |
| `==`     | Equal to                 | `5 == 5 → True` |
| `!=`     | Not equal to             | `5 != 3 → True` |
| `>`      | Greater than             | `7 > 5 → True`  |
| `<`      | Less than                | `3 < 5 → True`  |
| `>=`     | Greater than or equal to | `5 >= 5 → True` |
| `<=`     | Less than or equal to    | `4 <= 6 → True` |

#### **C. Logical Operators**

| Operator | Description                                      | Example                     |
| -------- | ------------------------------------------------ | --------------------------- |
| `and`    | Returns `True` if both conditions are true       | `(5 > 3 and 10 > 5) → True` |
| `or`     | Returns `True` if at least one condition is true | `(5 > 3 or 10 < 5) → True`  |
| `not`    | Reverses the boolean value                       | `not(5 > 3) → False`        |

<mark style="color:green;">**Practice Questions**</mark>

1. What is the difference between `/` and `//` operators in Python?
2. Predict the output of the following:

   ```python
   a = 5
   b = 2
   print(a ** b // a)
   ```
3. Write a program that takes two numbers as input and prints whether the first is greater than, less than, or equal to the second.

***

### 4. **String Operations**

Strings (`str`) are sequences of characters enclosed in quotes.

```python
text = "Hello, World!"
print(len(text))  # Length of the string
print(text.upper())  # Convert to uppercase
print(text.lower())  # Convert to lowercase
print(text[0:5])  # Slicing (first 5 characters)
```

**String Concatenation & Formatting**

```python
first_name = "Alice"
last_name = "Smith"
full_name = first_name + " " + last_name  # Concatenation
print(f"My name is {first_name} {last_name}")  # f-strings (modern formatting)
```

<mark style="color:green;">**Practice Questions**</mark>

1. Given a string `"Python Programming"`, write a program to:
   * Convert it to lowercase.
   * Find the index of the letter `"P"`.
   * Extract `"Programming"` from the string.
2. What is the difference between single quotes (`'`) and double quotes (`"`) in Python strings?
3. What will be the output of the following?

   ```python
   print("5" + "3")
   print("5" * 3)
   ```

**Next Steps:** Practice these concepts by solving the questions above before moving on to loops and control structures!

***

## <mark style="color:purple;">**Python Control Structures: Conditional Statements & Loops**</mark>

Control structures allow us to control the flow of execution in a program using **conditional statements** and **loops**. These are essential for decision-making and repetitive tasks.

***

### **1. Conditional Statements (if-elif-else)**

Python uses `if`, `elif` (else if), and `else` to make decisions based on conditions.

#### **Syntax:**

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

* The **if block** executes if the condition is `True`.
* The **elif block** executes if the previous conditions are `False`, but its condition is `True`.
* The **else block** executes only if all previous conditions are `False`.

#### <mark style="color:green;">**Practice Questions:**</mark>

1. Write a Python program to check if a number is even or odd.
2. Given three numbers, write a program to find the largest among them.
3. Predict the output:

   ```python
   a = 5
   b = 10
   if a > b:
       print("A is greater")
   elif a < b:
       print("B is greater")
   else:
       print("Both are equal")
   ```

***

### **2. Loops in Python**

Loops allow us to **execute a block of code multiple times**.

#### **A. The for Loop**

A `for` loop iterates over a sequence (list, tuple, string, etc.).

```python
for i in range(5):  # Loops from 0 to 4
    print("Iteration:", i)
```

**Looping Over a List:**

```python
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)
```

***

#### **B. The while Loop**

A `while` loop runs as long as a condition remains `True`.

```python
count = 1
while count <= 5:
    print("Count:", count)
    count += 1  # Increment count to avoid infinite loop
```

***

#### **C. Loop Control Statements**

Python provides statements to control loop execution:

| Statement  | Description                                       |
| ---------- | ------------------------------------------------- |
| `break`    | Exits the loop immediately                        |
| `continue` | Skips the current iteration and moves to the next |
| `pass`     | Acts as a placeholder (does nothing)              |

**Example: Using `break` and `continue`**

```python
for i in range(1, 6):
    if i == 3:
        continue  # Skip 3
    print(i)
```

```python
for i in range(1, 6):
    if i == 3:
        break  # Stop at 3
    print(i)
```

***

#### <mark style="color:green;">**Practice Questions:**</mark>

1. Write a Python program to print numbers from 1 to 10 using both `for` and `while` loops.
2. Write a program that prints only even numbers from 1 to 20.
3. Predict the output:

   ```python
   for i in range(3):
       for j in range(2):
           print(i, j)
   ```

***

**Next Steps:** Move on to **Functions & Data Structures** after practicing these concepts!

## <mark style="color:red;background-color:yellow;">**2. Functions, Modules, and Data Structures (≈8 Hours)**</mark>

Functions, modules, and data structures are the building blocks of Python programming. This section covers:

1. **Functions:** Writing reusable code with parameters and return values.
2. **Modules & Packages:** Organizing and reusing code efficiently.
3. **Core Data Structures:** Lists, tuples, dictionaries, and sets.

***

## <mark style="color:purple;">**Functions in Python**</mark>

Functions allow us to write reusable code, making programs more modular and efficient.

**1. Defining a Function**

A function is defined using the `def` keyword.

```python
def greet():
    print("Hello, World!")

greet()  # Calling the function
```

**2. Function Parameters & Arguments**

Functions can take **parameters** to accept input values.

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

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

**Types of Arguments:**

| Type                          | Example                                                                    |
| ----------------------------- | -------------------------------------------------------------------------- |
| **Positional Arguments**      | `greet("Alice")`                                                           |
| **Keyword Arguments**         | `greet(name="Alice")`                                                      |
| **Default Arguments**         | `def greet(name="Guest")`                                                  |
| **Variable-Length Arguments** | `*args` for multiple positional args, `**kwargs` for multiple keyword args |

```python
def add_numbers(*args):  # Accepts multiple numbers
    return sum(args)

print(add_numbers(2, 3, 5))  # Output: 10
```

**3. Return Values in Functions**

A function can return values using `return`.

```python
def square(num):
    return num ** 2

result = square(4)  
print(result)  # Output: 16
```

***

#### <mark style="color:green;">**Practice Questions**</mark>

1. Write a function that takes two numbers as input and returns their sum.
2. Modify the above function to support variable-length arguments (`*args`).
3. Predict the output of the following:

   ```python
   def test(a=5, b=10):
       return a * b
   print(test(3))  
   ```

***

## <mark style="color:purple;">**Modules & Packages: Organizing and Reusing Code Efficiently**</mark>

In Python, **modules** and **packages** help in organizing code, improving reusability, and maintaining clean project structures.

#### **1. Modules in Python**

A **module** is a Python file (`.py`) that contains functions, classes, or variables, which can be imported into other programs.

**1.1 Creating and Importing Modules**

1. **Create a module** (`math_utils.py`):

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

   def multiply(a, b):
       return a * b
   ```
2. **Import and use the module** (`main.py`):

   ```python
   import math_utils

   print(math_utils.add(3, 5))  # Output: 8
   print(math_utils.multiply(4, 2))  # Output: 8
   ```
3. **Importing specific functions**:

   ```python
   from math_utils import add

   print(add(10, 20))  # Output: 30
   ```

***

#### **2. Built-in Python Modules**

Python comes with many built-in modules. Some common ones include:

| Module     | Purpose                            |
| ---------- | ---------------------------------- |
| `math`     | Mathematical functions             |
| `random`   | Generate random numbers            |
| `datetime` | Work with dates and times          |
| `os`       | Interact with the operating system |

**Example: Using the `math` module**

```python
import math

print(math.sqrt(25))  # Output: 5.0
print(math.pi)  # Output: 3.141592653589793
```

***

#### **3. Python Packages**

A **package** is a collection of modules, organized using a directory with an `__init__.py` file.

**3.1 Creating a Package**

1. **Create a folder named `mypackage/`**
2. **Inside `mypackage/`, create `__init__.py`** (This makes it a package)
3. **Add modules**: Example, `mypackage/utils.py`

   ```python
   def greet(name):
       return f"Hello, {name}!"
   ```
4. **Import from the package**:

   ```python
   from mypackage import utils

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

***

#### <mark style="color:green;">**Practice Questions**</mark>

1. Create a module `calculator.py` with functions for addition, subtraction, and multiplication. Import it into another script and use the functions.
2. Use the `random` module to generate a random number between 1 and 100.
3. What will be the output of the following?

   ```python
   import datetime
   print(datetime.datetime.now().year)
   ```

***

## <mark style="color:purple;">**Core Data Structures: Lists, Tuples, Dictionaries, and Sets**</mark>

Python provides built-in data structures that help in storing and manipulating collections of data efficiently.

***

### **1. Lists (Ordered, Mutable, Allows Duplicates)**

A list is an ordered collection that can hold different data types and is **mutable** (modifiable).

#### **1.1 Creating and Accessing Lists**

```python
fruits = ["apple", "banana", "cherry"]
print(fruits[0])  # Output: apple
```

#### **1.2 List Methods**

```python
fruits.append("orange")  # Adds an item
fruits.remove("banana")  # Removes an item
print(fruits)  # Output: ['apple', 'cherry', 'orange']
```

#### **1.3 List Comprehension**

```python
squares = [x**2 for x in range(5)]
print(squares)  # Output: [0, 1, 4, 9, 16]
```

<mark style="color:green;">**Practice Questions**</mark>

1. Create a list of numbers and find the sum of all elements.
2. Write a Python program to remove duplicates from a list.

***

### **2. Tuples (Ordered, Immutable, Allows Duplicates)**

Tuples are similar to lists but **immutable** (cannot be modified).

#### **2.1 Creating and Accessing Tuples**

```python
coordinates = (10, 20)
print(coordinates[1])  # Output: 20
```

#### **2.2 Tuple Unpacking**

```python
x, y = coordinates
print(x, y)  # Output: 10 20
```

#### <mark style="color:green;">**Practice Questions**</mark>

1. Convert a list to a tuple and try modifying it.
2. Write a program to swap two values using tuple unpacking.

***

### **3. Dictionaries (Key-Value Pairs, Unordered, Mutable)**

A dictionary stores data in key-value pairs, making lookups efficient.

#### **3.1 Creating and Accessing Dictionaries**

```python
student = {"name": "Alice", "age": 22, "grade": "A"}
print(student["name"])  # Output: Alice
```

#### **3.2 Dictionary Methods**

```python
student["age"] = 23  # Modifying a value
student["city"] = "New York"  # Adding a new key-value pair
del student["grade"]  # Removing a key
```

#### <mark style="color:blue;">**Practice Questions**</mark>

1. Create a dictionary of students with their marks and find the student with the highest marks.
2. Write a Python script to merge two dictionaries.

***

### **4. Sets (Unordered, Unique Elements, Mutable)**

A set is an unordered collection that **does not allow duplicates**.

#### **4.1 Creating and Using Sets**

```python
numbers = {1, 2, 3, 4, 4, 2}
print(numbers)  # Output: {1, 2, 3, 4} (Duplicates removed)
```

#### **4.2 Set Operations**

```python
A = {1, 2, 3}
B = {3, 4, 5}

print(A | B)  # Union: {1, 2, 3, 4, 5}
print(A & B)  # Intersection: {3}
print(A - B)  # Difference: {1, 2}
```

#### <mark style="color:green;">**Practice Questions**</mark>

1. Write a Python program to remove duplicate values from a list using a set.
2. Given two sets, find their common elements and differences.

***

### <mark style="color:purple;">**Error Handling in Python: Introduction to try/except Blocks**</mark>

Errors (exceptions) can cause a program to crash if not handled properly. Python provides **try/except** blocks to handle exceptions gracefully.

***

### **1. What is an Exception?**

An **exception** is an error that occurs during execution. Some common ones:

| Exception           | Description                             |
| ------------------- | --------------------------------------- |
| `ZeroDivisionError` | Division by zero                        |
| `TypeError`         | Operation on incompatible types         |
| `ValueError`        | Invalid value for a function            |
| `IndexError`        | List index out of range                 |
| `KeyError`          | Accessing a non-existent dictionary key |

Example of an unhandled exception:

```python
print(5 / 0)  # ZeroDivisionError
```

***

### **2. Handling Errors with try/except**

#### **2.1 Basic try/except Usage**

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

#### **2.2 Using `else` and `finally`**

* **`else`** runs if no exception occurs.
* **`finally`** runs **always**, whether an exception occurs or not.

```python
try:
    num = int(input("Enter a number: "))
    result = 10 / num
except ZeroDivisionError:
    print("Cannot divide by zero!")
else:
    print("Result:", result)  # Runs only if no exception
finally:
    print("Execution completed.")  # Always runs
```

***

### **3. Raising Custom Exceptions**

Use `raise` to trigger custom exceptions:

```python
def check_age(age):
    if age < 18:
        raise ValueError("Age must be 18 or above!")
    print("Access granted.")

try:
    check_age(15)
except ValueError as e:
    print(e)
```

***

### **Practice Questions**

1. Write a function that takes a list and an index as input. Use try/except to handle **IndexError** if the index is out of range.
2. Modify a program to handle `KeyError` when accessing a dictionary key that doesn’t exist.
3. Predict the output:

   ```python
   try:
       print(10 / 2)
   except ZeroDivisionError:
       print("Error!")
   else:
       print("No error!")
   finally:
       print("Done!")
   ```

***

## <mark style="color:red;background-color:yellow;">Object-Oriented Programming (≈6 Hours)</mark>

#### **1. Defining a Class and Creating Objects**

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

car1 = Car("Toyota", "Corolla")
print(car1.brand)  # Output: Toyota
```

***

#### **2. Inheritance** (Reusing properties and methods from a parent class)

```python
class ElectricCar(Car):
    def __init__(self, brand, model, battery):
        super().__init__(brand, model)
        self.battery = battery

ecar = ElectricCar("Tesla", "Model S", "100 kWh")
print(ecar.battery)  # Output: 100 kWh
```

***

#### **3. Method Overriding** (Redefining a method in a child class)

```python
class Animal:
    def speak(self):
        return "Some sound"

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

dog = Dog()
print(dog.speak())  # Output: Bark
```

***

#### **4. Encapsulation** (Using private variables with `_` or `__`)

```python
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Private variable

    def get_balance(self):
        return self.__balance  # Getter method

account = BankAccount(1000)
print(account.get_balance())  # Output: 1000
```

***

#### **5. Polymorphism** (Using the same method name for different implementations)

```python
class Cat:
    def speak(self):
        return "Meow"

animals = [Dog(), Cat()]
for animal in animals:
    print(animal.speak())  
# Output: Bark Meow
```

***

#### **6. Abstract Classes** (Using `ABC` for defining abstract methods)

```python
from abc import ABC, abstractmethod

class Vehicle(ABC):
    @abstractmethod
    def start(self):
        pass

class Bike(Vehicle):
    def start(self):
        print("Bike started")

bike = Bike()
bike.start()  # Output: Bike started
```

#### **7. Magic Methods (`__str__`, `__len__`, etc.)**

Magic methods (also called dunder methods) allow objects to define behavior for built-in operations like printing, addition, and length calculations.

#### **Example: `__str__` and `__len__`**

```python
class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

    def __str__(self):
        return f"Book: {self.title}"

    def __len__(self):
        return self.pages

book = Book("Python Mastery", 300)
print(book)  # Output: Book: Python Mastery
print(len(book))  # Output: 300
```

***

#### **8. Multiple Inheritance**

Multiple inheritance allows a class to inherit from more than one parent class.

#### **Example: Inheriting from Two Classes**

```python
class Writer:
    def write(self):
        return "Writing content"

class Editor:
    def edit(self):
        return "Editing content"

class Author(Writer, Editor):
    pass

author = Author()
print(author.write())  # Output: Writing content
print(author.edit())  # Output: Editing content
```

***

#### **9. Composition**

Composition is when a class contains an instance of another class rather than inheriting from it.

#### **Example: Using Composition Instead of Inheritance**

```python
class Engine:
    def start(self):
        return "Engine started"

class Car:
    def __init__(self, brand):
        self.brand = brand
        self.engine = Engine()  # Creating an instance of Engine inside Car

    def start(self):
        return f"{self.brand} car: {self.engine.start()}"

car = Car("Toyota")
print(car.start())  # Output: Toyota car: Engine started
```

***

#### <mark style="color:green;">**Practice Questions**</mark>

1. Create a `Person` class with `name` and `age` attributes, and a method to display them.
2. Implement an `Employee` class that inherits from `Person` and adds a `salary` attribute.
3. Create a `Shape` class with a method `area()`, then implement `Rectangle` and `Circle` classes with their own `area()` calculations.

***

## <mark style="color:red;background-color:yellow;">4. Building a “Hello World” API with Flask (≈8 Hours)</mark>

## <mark style="color:purple;">**Simple static API**</mark>

#### **Steps to Run the API**

1. Install Flask (if not already installed):

   ```bash
   pip install flask
   ```
2. Save the following code as `app.py`.
3. Run the script using:

   ```bash
   python app.py
   ```
4. Open a browser or use Postman to test:
   * `http://127.0.0.1:5000/` → Home Route
   * `http://127.0.0.1:5000/data` → Static JSON Data

#### **Static API Code (`app.py`)**

```python
from flask import Flask, jsonify

app = Flask(__name__)

# Home Route
@app.route('/')
def home():
    return jsonify({"message": "Welcome to the Static API!"})

# Static JSON Data Route
@app.route('/data')
def get_data():
    return jsonify({
        "name": "Manvendra",
        "role": "Senior Developer",
        "skills": ["Python", "Java", "iOS", "Machine Learning"]
    })

# Run the API
if __name__ == '__main__':
    app.run(debug=True)
```

#### **Output Examples**

**1. Visiting `/` Route**

```json
{"message": "Welcome to the Static API!"}
```

**2. Visiting `/data` Route**

```json
{
    "name": "Manvendra",
    "role": "Senior Developer",
    "skills": ["Python", "Java", "iOS", "Machine Learning"]
}
```

This is a basic **static API** returning hardcoded JSON responses.

***

## <mark style="color:purple;">**Flask API with MongoDB (Dynamic Data)**</mark>

#### **1. Install Required Libraries**

```bash
pip install flask pymongo
```

#### **2. Setup MongoDB**

Ensure **MongoDB is running** locally or use a cloud database (MongoDB Atlas).

* **Local Connection URI:** `mongodb://localhost:27017/`
* **Cloud Connection URI (Atlas Example):** `mongodb+srv://<username>:<password>@cluster.mongodb.net/<database>?retryWrites=true&w=majority`

#### **3. API Code (`app.py`)**

```python
from flask import Flask, jsonify
from pymongo import MongoClient

app = Flask(__name__)

# Connect to MongoDB
client = MongoClient("mongodb://localhost:27017/")
db = client["testdb"]  # Database
collection = db["users"]  # Collection

# Insert Sample Data (Run once, then comment out)
# collection.insert_many([
#     {"name": "Manvendra", "role": "Senior Developer", "skills": ["Python", "Java"]},
#     {"name": "Alice", "role": "Data Scientist", "skills": ["Python", "ML", "AI"]}
# ])

# Fetch Data from MongoDB
@app.route('/users', methods=['GET'])
def get_users():
    users = list(collection.find({}, {"_id": 0}))  # Exclude MongoDB ObjectId
    return jsonify(users)

if __name__ == '__main__':
    app.run(debug=True)
```

#### **4. Test API Endpoints**

Start the API:

```bash
python app.py
```

Visit:

* `http://127.0.0.1:5000/users` → Fetches **dynamic data** from MongoDB.

#### **5. Example Output**

```json
[
    {
        "name": "Manvendra",
        "role": "Senior Developer",
        "skills": ["Python", "Java"]
    },
    {
        "name": "Alice",
        "role": "Data Scientist",
        "skills": ["Python", "ML", "AI"]
    }
]
```

***

## <mark style="color:purple;">Database Integration: MongoDB CRUD Operations (≈6 Hours)</mark>

#### **1. Install Required Libraries**

```bash
pip install flask pymongo
```

#### **2. API Code (`app.py`)**

```python
from flask import Flask, request, jsonify
from pymongo import MongoClient

app = Flask(__name__)

# Connect to MongoDB
client = MongoClient("mongodb://localhost:27017/")
db = client["testdb"]  # Database
collection = db["users"]  # Collection

# Create (Insert Data)
@app.route('/users', methods=['POST'])
def create_user():
    data = request.json
    collection.insert_one(data)
    return jsonify({"message": "User added successfully"}), 201

# Read (Get All Users)
@app.route('/users', methods=['GET'])
def get_users():
    users = list(collection.find({}, {"_id": 0}))  # Exclude MongoDB ObjectId
    return jsonify(users)

# Update (Modify an Existing User)
@app.route('/users/<string:name>', methods=['PUT'])
def update_user(name):
    data = request.json
    result = collection.update_one({"name": name}, {"$set": data})
    if result.matched_count:
        return jsonify({"message": "User updated successfully"})
    return jsonify({"error": "User not found"}), 404

# Delete (Remove a User)
@app.route('/users/<string:name>', methods=['DELETE'])
def delete_user(name):
    result = collection.delete_one({"name": name})
    if result.deleted_count:
        return jsonify({"message": "User deleted successfully"})
    return jsonify({"error": "User not found"}), 404

if __name__ == '__main__':
    app.run(debug=True)
```

#### **3. Testing the API**

Start the API:

```bash
python app.py
```

**Create a User (POST)**

```json
POST http://127.0.0.1:5000/users
Body: {"name": "John", "role": "Developer", "skills": ["Python", "Flask"]}
```

**Response:**

```json
{"message": "User added successfully"}
```

**Get All Users (GET)**

```json
GET http://127.0.0.1:5000/users
```

**Response Example:**

```json
[
    {"name": "John", "role": "Developer", "skills": ["Python", "Flask"]}
]
```

**Update a User (PUT)**

```json
PUT http://127.0.0.1:5000/users/John
Body: {"role": "Senior Developer"}
```

**Response:**

```json
{"message": "User updated successfully"}
```

**Delete a User (DELETE)**

```json
DELETE http://127.0.0.1:5000/users/John
```

**Response:**

```json
{"message": "User deleted successfully"}
```

## <mark style="color:red;background-color:yellow;">**6. Advanced Python Topics (≈8 Hours)**</mark>

#### **1. Decorators & Context Managers** (Enhancing functions and managing resources)

**1.1 Decorators (Modifying Function Behavior Without Changing Code)**

```python
def decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

@decorator
def greet():
    print("Hello, World!")

greet()
```

**Output:**

```
Before function call  
Hello, World!  
After function call  
```

**1.2 Context Managers (`with` Statement for Resource Management)**

```python
with open("file.txt", "w") as f:
    f.write("Hello, Python!")
```

* Ensures the file is **closed automatically** after execution.

**Practice Questions**

1. Create a decorator that logs the execution time of a function.
2. Implement a context manager for handling database connections.

***

#### **2. Iterators & Generators** (Efficient looping and lazy evaluation)

**2.1 Iterators (Custom Iteration Using `__iter__` and `__next__`)**

```python
class Counter:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        self.current += 1
        return self.current - 1

counter = Counter(1, 5)
for num in counter:
    print(num)
```

**2.2 Generators (Memory-Efficient Iterators Using `yield`)**

```python
def number_generator(n):
    for i in range(n):
        yield i

gen = number_generator(5)
print(next(gen))  # Output: 0
print(next(gen))  # Output: 1
```

* Unlike lists, **generators do not store all values in memory**.

**Practice Questions**

1. Create an iterator that returns even numbers up to a limit.
2. Write a generator function to yield Fibonacci numbers.

***

#### **3. Testing & Debugging** (Ensuring code correctness and finding errors)

**3.1 Unit Testing with `unittest`**

```python
import unittest

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

class TestMathOperations(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)

if __name__ == "__main__":
    unittest.main()
```

**3.2 Debugging with `pdb` (Python Debugger)**

```python
import pdb

def buggy_function():
    x = 10
    y = 0
    pdb.set_trace()  # Debugging breakpoint
    return x / y  # Causes ZeroDivisionError

# buggy_function()  # Uncomment to debug
```

**Practice Questions**

1. Write unit tests for a function that checks if a string is a palindrome.
2. Use Python's `pdb` module to debug a function that calculates factorial.

***

### <mark style="color:red;background-color:yellow;">**Machine Learning with Python (≈30–40 Hours)**</mark>

This section introduces **Machine Learning (ML) with Python**, covering essential libraries, data preprocessing, model training, and evaluation.

#### **1. Setting Up the ML Environment** (≈2 Hours)

* Install key libraries:

  ```bash
  pip install numpy pandas matplotlib seaborn scikit-learn
  ```
* Importing ML essentials:

  ```python
  import numpy as np  
  import pandas as pd  
  import matplotlib.pyplot as plt  
  import seaborn as sns  
  from sklearn.model_selection import train_test_split  
  ```

#### **2. Data Preprocessing & Exploration** (≈6 Hours)

* **Loading Data:**

  ```python
  df = pd.read_csv("data.csv")  
  print(df.head())  
  ```
* **Handling Missing Values:**

  ```python
  df.fillna(df.mean(), inplace=True)  
  ```
* **Feature Scaling:**

  ```python
  from sklearn.preprocessing import StandardScaler  
  scaler = StandardScaler()  
  df_scaled = scaler.fit_transform(df)  
  ```

&#x20;**Practice Questions**

1. Load a dataset and check for missing values.
2. Normalize numerical features using `MinMaxScaler`.

#### **3. Supervised Learning: Regression & Classification** (≈10 Hours)

**3.1 Linear Regression (Predicting Continuous Values)**

```python
from sklearn.linear_model import LinearRegression  

X = df[["feature1", "feature2"]]  
y = df["target"]  
model = LinearRegression()  
model.fit(X, y)  
print(model.coef_, model.intercept_)  
```

**3.2 Logistic Regression (Binary Classification)**

```python
from sklearn.linear_model import LogisticRegression  

model = LogisticRegression()  
model.fit(X, y)  
print(model.predict([[5, 3]]))  
```

**Practice Questions**

1. Train a linear regression model to predict house prices.
2. Build a classifier to predict if an email is spam or not.

#### **4. Unsupervised Learning: Clustering & Dimensionality Reduction** (≈8 Hours)

**4.1 K-Means Clustering (Grouping Similar Data)**

```python
from sklearn.cluster import KMeans  

kmeans = KMeans(n_clusters=3)  
kmeans.fit(df)  
df["Cluster"] = kmeans.labels_  
```

**4.2 Principal Component Analysis (PCA) for Dimensionality Reduction**

```python
from sklearn.decomposition import PCA  

pca = PCA(n_components=2)  
df_pca = pca.fit_transform(df)  
```

**Practice Questions**

1. Apply K-Means to group customers based on purchasing behavior.
2. Use PCA to reduce the dimensions of a dataset with 10+ features.

#### **5. Model Evaluation & Hyperparameter Tuning** (≈6 Hours)

* **Splitting Data:**

  ```python
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  
  ```
* **Evaluating with Metrics:**

  ```python
  from sklearn.metrics import accuracy_score, mean_squared_error  

  y_pred = model.predict(X_test)  
  print("Accuracy:", accuracy_score(y_test, y_pred))  
  ```
* **Hyperparameter Tuning using GridSearchCV:**

  ```python
  from sklearn.model_selection import GridSearchCV  

  param_grid = {"C": [0.1, 1, 10]}  
  grid = GridSearchCV(LogisticRegression(), param_grid)  
  grid.fit(X_train, y_train)  
  print(grid.best_params_)  
  ```

**Practice Questions**

1. Evaluate a trained model using `R² score` or `F1-score`.
2. Use `GridSearchCV` to optimize hyperparameters for an SVM model.

#### **6. Deep Learning with TensorFlow & Keras (Optional)** (≈8 Hours)

```bash
pip install tensorflow keras
```

```python
import tensorflow as tf  
from tensorflow import keras  

model = keras.Sequential([  
    keras.layers.Dense(128, activation="relu"),  
    keras.layers.Dense(1, activation="sigmoid")  
])  
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])  
```

**Practice Questions**

1. Train a neural network on the MNIST dataset.
2. Implement a CNN for image classification.

***

#### **Next Steps:**&#x20;

Want to work on **real-world ML projects** or explore **NLP & Computer Vision**?
