From 900741a7f277e582a7547d3b57bf8988f48c0d37 Mon Sep 17 00:00:00 2001
From: byteninja01 <186124410+byteninja01@users.noreply.github.com.>
Date: Sat, 4 Oct 2025 11:06:58 +0530
Subject: [PATCH 1/2] Added interactive quizzes in Python docs modules
---
docs/python/conditional-statements-python.md | 210 +++-
docs/python/datatype-python.md | 209 +++-
docs/python/intro-python.md | 202 +++-
docs/python/python-array.md | 284 +++++-
docs/python/python-casting.md | 273 ++++-
docs/python/python-dictionaries.md | 455 ++++++++-
docs/python/python-errors-and-exceptions.md | 451 +++++++++
docs/python/python-functions.md | 298 ++++++
docs/python/python-io.md | 393 +++++++-
docs/python/python-libraries.md | 767 ++++++++++++++
docs/python/python-list.md | 433 +++++++-
docs/python/python-loops.md | 649 +++++++++++-
docs/python/python-oops.md | 925 ++++++++++++++++-
docs/python/python-recursion.md | 856 +++++++++++++++-
docs/python/python-set.md | 662 ++++++++++++-
docs/python/python-simple-project.md | 990 +++++++++++++++++++
docs/python/python-string.md | 868 +++++++++++++++-
docs/python/python-syntax.md | 216 +++-
docs/python/python-tuple.md | 215 +++-
docs/python/python-variables.md | 229 ++++-
docs/python/python_oops.md | 251 +++++
docs/python/python_operators.md | 281 +++++-
22 files changed, 10046 insertions(+), 71 deletions(-)
create mode 100644 docs/python/python-libraries.md
create mode 100644 docs/python/python-simple-project.md
diff --git a/docs/python/conditional-statements-python.md b/docs/python/conditional-statements-python.md
index db006c23..c81a21ec 100644
--- a/docs/python/conditional-statements-python.md
+++ b/docs/python/conditional-statements-python.md
@@ -80,6 +80,37 @@ else:
---
+### 🧠 Quiz 1: Basic Conditionals
+
+**Question 1:** What will be the output of the following code?
+```python
+x = 7
+if x > 10:
+ print("Large")
+else:
+ print("Small")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Small`
+
+**Explanation:** Since 7 is not greater than 10, the condition `x > 10` is False, so the `else` block executes and prints "Small".
+
+
+**Question 2:** What does `elif` stand for in Python?
+
+
+Click to reveal answer
+
+**Answer:** "else if"
+
+**Explanation:** `elif` is short for "else if" and is used to check multiple conditions in sequence.
+
+
+---
+
## Nested `if` Statements
You can put an `if` statement inside another `if` statement.
@@ -110,6 +141,44 @@ Adult
---
+### 🧠 Quiz 2: Ternary Operator & Nested Conditions
+
+**Question 1:** What will the variable `result` contain after this code runs?
+```python
+temperature = 25
+result = "Hot" if temperature > 30 else "Cold"
+```
+
+
+Click to reveal answer
+
+**Answer:** `"Cold"`
+
+**Explanation:** Since 25 is not greater than 30, the condition is False, so the value after `else` ("Cold") is assigned to `result`.
+
+
+**Question 2:** What will be printed by this nested if statement?
+```python
+x = 12
+if x > 5:
+ if x < 15:
+ print("Valid")
+ else:
+ print("Too high")
+else:
+ print("Too low")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Valid`
+
+**Explanation:** First, `x > 5` is True (12 > 5), so we enter the outer if block. Then, `x < 15` is also True (12 < 15), so "Valid" is printed.
+
+
+---
+
## Logical Operators in Conditions
You can combine multiple conditions using `and`, `or`, and `not`.
@@ -140,6 +209,45 @@ if "apple" in fruits:
---
+### 🧠 Quiz 3: Logical Operators
+
+**Question 1:** What will be the output?
+```python
+a = 10
+b = 20
+if a > 5 and b < 15:
+ print("Both true")
+else:
+ print("At least one false")
+```
+
+
+Click to reveal answer
+
+**Answer:** `At least one false`
+
+**Explanation:** While `a > 5` is True (10 > 5), `b < 15` is False (20 is not less than 15). Since `and` requires both conditions to be True, the result is False and the else block executes.
+
+
+**Question 2:** What does this code print?
+```python
+x = 8
+if not x == 8:
+ print("Not eight")
+else:
+ print("Is eight")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Is eight`
+
+**Explanation:** `x == 8` is True, but the `not` operator negates it to False. So the if condition fails and the else block runs, printing "Is eight".
+
+
+---
+
## Indentation Rules
In Python, indentation is important for defining code blocks.
@@ -178,7 +286,7 @@ Write a Python program to check whether a number is **even** or **odd**.
#### 3. **Age Eligibility for Voting**
-Write a program to take a person’s age as input and check if they are **eligible to vote** (18 years or older).
+Write a program to take a person's age as input and check if they are **eligible to vote** (18 years or older).
#### 4. **Largest of Two Numbers**
@@ -229,10 +337,104 @@ Write a program to input a single character and check whether it is:
Write a program that asks the user to enter a **username** and **password**.
-* If the username is `"admin"` and the password is `"12345"`, print **“Login Successful”**.
-* Otherwise, print **“Invalid credentials”**.
+* If the username is `"admin"` and the password is `"12345"`, print **"Login Successful"**.
+* Otherwise, print **"Invalid credentials"**.
+
+---
+
+### 🧠 Quiz 4: Code Output Prediction
+
+**Question 1:** What will this code output?
+```python
+marks = 82
+if marks >= 90:
+ print("A")
+elif marks >= 75:
+ print("B")
+elif marks >= 60:
+ print("C")
+else:
+ print("D")
+```
+
+
+Click to reveal answer
+
+**Answer:** `B`
+
+**Explanation:** The code checks conditions in order. Since 82 is not ≥ 90 but is ≥ 75, it prints "B" and stops checking further conditions.
+
+
+**Question 2:** Predict the output:
+```python
+fruits = ["apple", "banana", "cherry"]
+if "mango" in fruits:
+ print("Found")
+else:
+ print("Not found")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Not found`
+
+**Explanation:** "mango" is not in the fruits list, so the condition is False and the else block executes.
+
+---
+
+### 🧠 Quiz 5: Challenge Questions
+
+**Question 1:** What's wrong with this code?
+```python
+x = 5
+if x > 3:
+print("Greater")
+```
+
+
+Click to reveal answer
+
+**Answer:** Missing indentation
+
+**Explanation:** The print statement must be indented to be inside the if block. Python uses indentation to define code blocks, and this code will raise an `IndentationError`.
+
+
+**Question 2:** What will be printed?
+```python
+year = 2024
+if year % 4 == 0 and year % 100 != 0:
+ print("Leap")
+elif year % 400 == 0:
+ print("Leap")
+else:
+ print("Not Leap")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Leap`
+
+**Explanation:** 2024 is divisible by 4 (2024 % 4 == 0) and not divisible by 100 (2024 % 100 == 24, not 0), so the first condition is True and "Leap" is printed.
+
+
+**Question 3:** Write a single line of code using a ternary operator to assign "Pass" to a variable `result` if `score` is 50 or more, otherwise assign "Fail".
+
+
+Click to reveal answer
+
+**Answer:**
+```python
+result = "Pass" if score >= 50 else "Fail"
+```
+
+**Explanation:** The ternary operator format is `value_if_true if condition else value_if_false`.
+
+
+---
## Conclusion
-Conditional statements are essential for decision-making in programs. Mastering `if`, `elif`, and `else` allows you to control your program's logic effectively.
+Conditional statements are essential for decision-making in programs. Mastering `if`, `elif`, and `else` allows you to control your program's logic effectively.
\ No newline at end of file
diff --git a/docs/python/datatype-python.md b/docs/python/datatype-python.md
index bc84d704..538eb058 100644
--- a/docs/python/datatype-python.md
+++ b/docs/python/datatype-python.md
@@ -42,7 +42,7 @@ A sequence of Unicode characters.
```python
name = "Dhruba"
-````
+```
You can perform operations like:
@@ -80,6 +80,44 @@ z = 2 + 3j
---
+### 🧠 Quiz 1: Text and Numeric Types
+
+**Question 1:** What data type is the value `"123"`?
+
+
+Click to reveal answer
+
+**Answer:** `str` (string)
+
+**Explanation:** Even though it contains digits, the quotes make it a string, not an integer. You can verify with `type("123")`.
+
+
+**Question 2:** What will `type(3.0)` return?
+
+
+Click to reveal answer
+
+**Answer:** ``
+
+**Explanation:** Even though 3.0 equals 3, the decimal point makes it a float, not an int.
+
+
+**Question 3:** Which of the following is a valid complex number in Python?
+- A) `5 + 3i`
+- B) `5 + 3j`
+- C) `complex(5, 3)`
+- D) Both B and C
+
+
+Click to reveal answer
+
+**Answer:** D) Both B and C
+
+**Explanation:** Python uses `j` (not `i`) for the imaginary part. You can create complex numbers using either `5 + 3j` or `complex(5, 3)`.
+
+
+---
+
## Sequence Types
### `list`
@@ -108,6 +146,47 @@ nums = range(5)
---
+### 🧠 Quiz 2: Sequence Types
+
+**Question 1:** What's the main difference between a list and a tuple?
+
+
+Click to reveal answer
+
+**Answer:** Lists are mutable (can be changed), tuples are immutable (cannot be changed)
+
+**Explanation:** After creation, you can modify a list (`fruits[0] = "orange"`), but you cannot modify a tuple. Attempting to change a tuple will raise a `TypeError`.
+
+
+**Question 2:** What will be the output of `list(range(3))`?
+
+
+Click to reveal answer
+
+**Answer:** `[0, 1, 2]`
+
+**Explanation:** `range(3)` generates numbers from 0 up to (but not including) 3. Converting it to a list gives `[0, 1, 2]`.
+
+
+**Question 3:** Which operation is valid?
+```python
+my_tuple = (1, 2, 3)
+```
+- A) `my_tuple[0] = 5`
+- B) `my_tuple.append(4)`
+- C) `len(my_tuple)`
+- D) `my_tuple.sort()`
+
+
+Click to reveal answer
+
+**Answer:** C) `len(my_tuple)`
+
+**Explanation:** Tuples are immutable, so you cannot modify them (options A, B, D would raise errors). However, you can check their length with `len()`, which returns 3.
+
+
+---
+
## Mapping Type: `dict`
Unordered collection of key-value pairs:
@@ -141,6 +220,46 @@ readonly_ids = frozenset([1, 2, 3])
---
+### 🧠 Quiz 3: Mapping and Set Types
+
+**Question 1:** What happens if you try to add duplicate values to a set?
+```python
+my_set = {1, 2, 3, 2, 1}
+```
+
+
+Click to reveal answer
+
+**Answer:** Duplicates are automatically removed; `my_set` will be `{1, 2, 3}`
+
+**Explanation:** Sets only store unique values. When you create a set with duplicates, Python automatically keeps only one instance of each value.
+
+
+**Question 2:** How do you access the value associated with key `"name"` in this dictionary?
+```python
+person = {"name": "Alice", "age": 30}
+```
+
+
+Click to reveal answer
+
+**Answer:** `person["name"]` or `person.get("name")`
+
+**Explanation:** You can access dictionary values using square brackets with the key, or use the `.get()` method. Both return `"Alice"`.
+
+
+**Question 3:** What's the main difference between `set` and `frozenset`?
+
+
+Click to reveal answer
+
+**Answer:** `set` is mutable (can be modified), `frozenset` is immutable (cannot be changed)
+
+**Explanation:** You can add or remove elements from a set using `.add()` or `.remove()`, but frozenset doesn't allow any modifications after creation. Frozensets can also be used as dictionary keys.
+
+
+---
+
## Boolean Type: `bool`
Only `True` or `False`:
@@ -189,6 +308,40 @@ response = None
---
+### 🧠 Quiz 4: Boolean, Binary, and None Types
+
+**Question 1:** What will `bool(0)` return?
+
+
+Click to reveal answer
+
+**Answer:** `False`
+
+**Explanation:** In Python, the number 0, empty strings, empty lists, and `None` are considered "falsy" and convert to `False`. All other numbers convert to `True`.
+
+
+**Question 2:** What is the output of `bool([])` (empty list)?
+
+
+Click to reveal answer
+
+**Answer:** `False`
+
+**Explanation:** Empty collections (lists, tuples, dictionaries, sets) are falsy in Python and evaluate to `False` when converted to boolean.
+
+
+**Question 3:** What does `None` represent in Python?
+
+
+Click to reveal answer
+
+**Answer:** The absence of a value or null value
+
+**Explanation:** `None` is Python's way of representing "nothing" or "no value". It's commonly used to initialize variables or indicate that a function doesn't return anything explicitly.
+
+
+---
+
## Type Checking and Conversion
### Check type
@@ -207,6 +360,60 @@ list("abc") # Output: ['a', 'b', 'c']
---
+### 🧠 Quiz 5: Type Checking and Conversion
+
+**Question 1:** What will `int("3.14")` produce?
+
+
+Click to reveal answer
+
+**Answer:** It will raise a `ValueError`
+
+**Explanation:** `int()` cannot directly convert a string containing a decimal point. You need to first convert to float: `int(float("3.14"))` which gives 3.
+
+
+**Question 2:** What is the result of `str([1, 2, 3])`?
+
+
+Click to reveal answer
+
+**Answer:** `"[1, 2, 3]"` (a string)
+
+**Explanation:** The `str()` function converts the list to its string representation, including the square brackets. The result is a string, not a list.
+
+
+**Question 3:** What will `list("Python")` return?
+
+
+Click to reveal answer
+
+**Answer:** `['P', 'y', 't', 'h', 'o', 'n']`
+
+**Explanation:** Converting a string to a list splits it into individual characters. Each character becomes a separate element in the list.
+
+
+**Question 4:** How do you check if a variable `x` is of type `int`?
+
+
+Click to reveal answer
+
+**Answer:** Use `type(x) == int` or `isinstance(x, int)`
+
+**Explanation:** Both methods work, but `isinstance()` is generally preferred as it also accounts for inheritance. Example: `isinstance(5, int)` returns `True`.
+
+
+**Question 5:** What happens when you convert `True` to an integer: `int(True)`?
+
+
+Click to reveal answer
+
+**Answer:** It returns `1`
+
+**Explanation:** In Python, `True` is treated as 1 and `False` as 0 when converted to integers. This is because `bool` is a subclass of `int`. Similarly, `int(False)` returns `0`.
+
+
+---
+
## Conclusion
Python provides a variety of built-in data types to handle data in efficient and expressive ways. Knowing when and how to use each data type is essential for writing clean and effective Python code.
\ No newline at end of file
diff --git a/docs/python/intro-python.md b/docs/python/intro-python.md
index 28552ac1..ebd5924f 100644
--- a/docs/python/intro-python.md
+++ b/docs/python/intro-python.md
@@ -38,6 +38,44 @@ Python is a high-level, interpreted, and general-purpose programming language. I
---
+### 🧠 Quiz 1: Python Basics
+
+**Question 1:** Who created Python and in which year was it first released?
+
+
+Click to reveal answer
+
+**Answer:** Guido van Rossum in 1991
+
+**Explanation:** Python was created by Guido van Rossum and first released in 1991. He is often referred to as Python's "Benevolent Dictator for Life" (BDFL).
+
+
+**Question 2:** What does it mean that Python is an "interpreted" language?
+
+
+Click to reveal answer
+
+**Answer:** Code is executed line-by-line without needing to be compiled first
+
+**Explanation:** Unlike compiled languages (like C or C++), Python code doesn't need to be converted to machine code before running. The Python interpreter reads and executes the code directly, making development faster and more flexible.
+
+
+**Question 3:** Which of the following is NOT a use case for Python?
+- A) Web Development
+- B) Machine Learning
+- C) Low-level hardware programming
+- D) Data Science
+
+
+Click to reveal answer
+
+**Answer:** C) Low-level hardware programming
+
+**Explanation:** Python is a high-level language best suited for applications like web development, data science, and machine learning. For low-level hardware programming (like device drivers), languages like C or Assembly are more appropriate.
+
+
+---
+
### Python Syntax Example
Here is a simple Python program that prints "Hello, World!":
@@ -45,7 +83,7 @@ Here is a simple Python program that prints "Hello, World!":
```python
print("Hello, World!")
-````
+```
Output:
@@ -55,6 +93,34 @@ Hello, World!
You can run this code in any Python interpreter, and it will display the message on the screen.
+---
+
+### 🧠 Quiz 2: Python Syntax
+
+**Question 1:** What will be the output of this code?
+```python
+print("Welcome to Python")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Welcome to Python`
+
+**Explanation:** The `print()` function displays the text inside the quotes. The output will be exactly: `Welcome to Python`
+
+
+**Question 2:** What is the correct file extension for Python files?
+
+
+Click to reveal answer
+
+**Answer:** `.py`
+
+**Explanation:** Python source code files are saved with the `.py` extension, for example: `hello.py`, `main.py`, etc.
+
+
+---
### Good to know
@@ -71,13 +137,52 @@ You can run this code in any Python interpreter, and it will display the message
3. Follow the installation instructions.
4. Verify installation by typing `python --version` in the terminal or command prompt.
+---
+
+### 🧠 Quiz 3: Python Environment
+
+**Question 1:** Which command is used to check if Python is installed and what version you have?
+
+
+Click to reveal answer
+
+**Answer:** `python --version` or `python -V`
+
+**Explanation:** Running this command in your terminal/command prompt will display your Python version, for example: `Python 3.11.0`
+
+
+**Question 2:** What is the current major version of Python that is actively maintained?
+
+
+Click to reveal answer
+
+**Answer:** Python 3
+
+**Explanation:** Python 3 is the current and actively maintained version. Python 2 reached end-of-life in January 2020 and is no longer supported with security updates.
+
+
+**Question 3:** Which of the following is a popular IDE for Python development?
+- A) MS Word
+- B) PyCharm
+- C) Adobe Photoshop
+- D) Windows Media Player
+
+
+Click to reveal answer
+
+**Answer:** B) PyCharm
+
+**Explanation:** PyCharm is a popular Integrated Development Environment (IDE) specifically designed for Python. Other popular options include VS Code, Thonny, and Jupyter Notebook.
+
+
+---
### Why Python is So Popular?
Python has gained massive popularity across industries and educational institutions because of the following reasons:
* **Simple and Readable Syntax**
- Python’s syntax is easy to read and write, similar to plain English, making it beginner-friendly.
+ Python's syntax is easy to read and write, similar to plain English, making it beginner-friendly.
* **Versatile and Multi-purpose**
Python is used in various fields including web development, data analysis, machine learning, artificial intelligence, automation, scripting, and more.
@@ -94,7 +199,98 @@ Python has gained massive popularity across industries and educational instituti
* **Used by Top Companies**
Companies like Google, Netflix, Facebook, NASA, and Dropbox use Python for various applications, proving its reliability and scalability.
+---
+
+### 🧠 Quiz 4: Python Popularity & Applications
+
+**Question 1:** Which of these companies is known to use Python?
+- A) Google
+- B) Netflix
+- C) NASA
+- D) All of the above
+
+
+Click to reveal answer
+
+**Answer:** D) All of the above
+
+**Explanation:** Google, Netflix, NASA, and many other major companies use Python for various purposes including web services, data analysis, automation, and scientific computing.
+
+
+**Question 2:** What is one reason Python is considered beginner-friendly?
+
+
+Click to reveal answer
+
+**Answer:** Its syntax is simple and similar to plain English
+
+**Explanation:** Python's syntax is designed to be readable and straightforward. For example, to print something, you simply write `print("Hello")`, which is very intuitive for beginners.
+
+
+**Question 3:** Python is "dynamically typed." What does this mean?
+
+
+Click to reveal answer
+
+**Answer:** You don't need to declare variable types explicitly; Python determines them automatically
+
+**Explanation:** In Python, you can write `x = 5` without declaring that x is an integer. Python automatically figures out the type. In contrast, languages like C++ require you to declare types: `int x = 5;`
+
+
+---
+
+### 🧠 Quiz 5: Python Libraries & Frameworks
+
+**Question 1:** Which Python library is commonly used for data analysis and manipulation?
+
+
+Click to reveal answer
+
+**Answer:** pandas
+
+**Explanation:** pandas is one of the most popular Python libraries for data analysis, providing data structures like DataFrames for efficient data manipulation. Other data science libraries include NumPy and matplotlib.
+
+
+**Question 2:** Match the Python framework/library with its primary use:
+
+
+Click to reveal answer
+
+**Answer:**
+- Django → Web Development
+- TensorFlow → Machine Learning/AI
+- Pygame → Game Development
+- NumPy → Scientific Computing/Data Science
+
+**Explanation:** Python's ecosystem includes specialized libraries and frameworks for different domains, making it versatile for various applications.
+
+
+**Question 3:** What makes Python "cross-platform"?
+
+
+Click to reveal answer
+
+**Answer:** Python code can run on different operating systems (Windows, macOS, Linux) without modification
+
+**Explanation:** You can write Python code on Windows and run the same code on macOS or Linux without changes. This portability is one of Python's key advantages.
+
+
+**Question 4:** Which statement about Python's standard library is TRUE?
+- A) It's very limited and requires many external downloads
+- B) It's rich and includes modules for many common tasks
+- C) It only supports basic math operations
+- D) It needs to be purchased separately
+
+
+Click to reveal answer
+
+**Answer:** B) It's rich and includes modules for many common tasks
+
+**Explanation:** Python's standard library is extensive and includes modules for file I/O, system calls, networking, data manipulation, and much more - all available without installing anything extra. This is often described as Python having "batteries included."
+
+
+---
## Conclusion
-Python is a powerful and versatile programming language that is easy to learn and widely used across different domains. Its simple syntax, vast libraries, and strong community support make it an excellent choice for both beginners and experienced developers.
+Python is a powerful and versatile programming language that is easy to learn and widely used across different domains. Its simple syntax, vast libraries, and strong community support make it an excellent choice for both beginners and experienced developers.
\ No newline at end of file
diff --git a/docs/python/python-array.md b/docs/python/python-array.md
index 48c98a28..4e146485 100644
--- a/docs/python/python-array.md
+++ b/docs/python/python-array.md
@@ -63,6 +63,55 @@ Arrays in Python require a **type code** to specify the element type:
---
+### 🧠 Quiz 1: Array Basics
+
+**Question 1:** What is the main difference between Python arrays and lists?
+
+
+Click to reveal answer
+
+**Answer:** Arrays store elements of the same data type and are more memory-efficient, while lists can store mixed data types
+
+**Explanation:** Arrays are type-restricted (e.g., all integers or all floats), making them faster and more memory-efficient for numerical operations. Lists can contain mixed types like `[1, "hello", 3.14, True]`.
+
+
+**Question 2:** What must you do before using arrays in Python?
+
+
+Click to reveal answer
+
+**Answer:** Import the `array` module using `import array`
+
+**Explanation:** Unlike lists which are built-in, arrays require importing the array module first: `import array` before you can use `array.array()`.
+
+
+**Question 3:** Which type code would you use to create an array of floating-point numbers?
+
+
+Click to reveal answer
+
+**Answer:** `'f'` for float or `'d'` for double
+
+**Explanation:**
+- `'f'` creates a float array (4 bytes): `array.array('f', [1.1, 2.2])`
+- `'d'` creates a double array (8 bytes, more precision): `array.array('d', [1.1, 2.2])`
+
+
+**Question 4:** What will happen if you try to create this array?
+```python
+mixed = array.array('i', [1, 2, 3.5, 4])
+```
+
+
+Click to reveal answer
+
+**Answer:** It will raise a `TypeError`
+
+**Explanation:** The type code `'i'` specifies integers only. Trying to add a float (3.5) to an integer array will cause an error. Arrays enforce type consistency.
+
+
+---
+
## Indexing
Just like lists, arrays use **zero-based indexing**.
@@ -106,6 +155,40 @@ print(nums) # array('i', [10, 99, 30, 40, 50])
---
+### 🧠 Quiz 2: Indexing and Slicing
+
+**Question 1:** Given `arr = array.array('i', [5, 10, 15, 20, 25])`, what is `arr[-2]`?
+
+
+Click to reveal answer
+
+**Answer:** `20`
+
+**Explanation:** Negative indexing counts from the end. `-1` is the last element (25), `-2` is the second-to-last element (20).
+
+
+**Question 2:** What will `arr[1:4]` return from the array `[5, 10, 15, 20, 25]`?
+
+
+Click to reveal answer
+
+**Answer:** `array('i', [10, 15, 20])`
+
+**Explanation:** Slicing `[1:4]` takes elements from index 1 up to (but not including) index 4. So it includes indices 1, 2, and 3, which are 10, 15, and 20.
+
+
+**Question 3:** What does `arr[::2]` do?
+
+
+Click to reveal answer
+
+**Answer:** Returns every second element (elements at even indices)
+
+**Explanation:** The syntax `[start:stop:step]` with step=2 takes every second element. From `[5, 10, 15, 20, 25]`, it returns `[5, 15, 25]`.
+
+
+---
+
## Array Methods
Python's `array` module provides several useful methods:
@@ -165,6 +248,60 @@ print(nums) # array('i', [1, 2, 4, 5])
---
+### 🧠 Quiz 3: Array Methods
+
+**Question 1:** What's the difference between `append()` and `extend()`?
+
+
+Click to reveal answer
+
+**Answer:** `append()` adds a single element, `extend()` adds multiple elements from an iterable
+
+**Explanation:**
+```python
+arr = array.array('i', [1, 2])
+arr.append(3) # [1, 2, 3]
+arr.extend([4, 5]) # [1, 2, 3, 4, 5]
+```
+
+
+**Question 2:** What will be the result?
+```python
+arr = array.array('i', [10, 20, 30, 20, 40])
+arr.remove(20)
+print(arr)
+```
+
+
+Click to reveal answer
+
+**Answer:** `array('i', [10, 30, 20, 40])`
+
+**Explanation:** `remove()` only removes the FIRST occurrence of the value. The second 20 remains in the array.
+
+
+**Question 3:** What does `pop()` return when called without an argument?
+
+
+Click to reveal answer
+
+**Answer:** It removes and returns the last element of the array
+
+**Explanation:** `pop()` without an index removes and returns the last element. `pop(i)` removes and returns the element at index `i`.
+
+
+**Question 4:** Given `arr = array.array('i', [5, 10, 15, 10, 20])`, what will `arr.count(10)` return?
+
+
+Click to reveal answer
+
+**Answer:** `2`
+
+**Explanation:** The `count()` method returns how many times the value appears in the array. The number 10 appears twice.
+
+
+---
+
## Iterating Through an Array
**Using a for loop:**
@@ -227,6 +364,71 @@ print(c) # array('i', [1, 2, 3, 4, 5])
---
+### 🧠 Quiz 4: Array Operations
+
+**Question 1:** What will be printed?
+```python
+arr = array.array('i', [1, 2, 3])
+arr2 = arr
+arr2.append(4)
+print(arr)
+```
+
+
+Click to reveal answer
+
+**Answer:** `array('i', [1, 2, 3, 4])`
+
+**Explanation:** When you do `arr2 = arr`, both variables point to the same array in memory. Modifying `arr2` also modifies `arr`. This is called a shallow copy or reference.
+
+
+**Question 2:** How do you create an independent copy of an array?
+
+
+Click to reveal answer
+
+**Answer:** Use `array.array(original.typecode, original)`
+
+**Explanation:**
+```python
+original = array.array('i', [1, 2, 3])
+copy = array.array(original.typecode, original)
+```
+This creates a new array with the same type code and values, but in a different memory location.
+
+
+**Question 3:** What will this code output?
+```python
+arr = array.array('i', [10, 20, 30])
+for i, val in enumerate(arr):
+ print(f"Index {i}: {val}")
+```
+
+
+Click to reveal answer
+
+**Answer:**
+```
+Index 0: 10
+Index 1: 20
+Index 2: 30
+```
+
+**Explanation:** `enumerate()` returns both the index and value during iteration, allowing you to access both at the same time.
+
+
+**Question 4:** What does the membership test `5 in arr` return for `arr = array.array('i', [1, 2, 3, 4])`?
+
+
+Click to reveal answer
+
+**Answer:** `False`
+
+**Explanation:** The `in` operator checks if a value exists in the array. Since 5 is not in the array `[1, 2, 3, 4]`, it returns `False`.
+
+
+---
+
### **Practice Questions**
1. **Basic Traversal**
@@ -265,7 +467,87 @@ print(c) # array('i', [1, 2, 3, 4, 5])
---
+### 🧠 Quiz 5: Problem-Solving Challenge
+
+**Question 1:** How would you find the maximum element in an array without using `max()`?
+
+
+Click to reveal answer
+
+**Answer:**
+```python
+arr = array.array('i', [5, 2, 9, 1, 7])
+max_val = arr[0]
+for num in arr:
+ if num > max_val:
+ max_val = num
+print(max_val) # 9
+```
+
+**Explanation:** Start with the first element as the maximum, then iterate through the array comparing each element. Update max_val whenever you find a larger number.
+
+
+**Question 2:** What's an efficient way to reverse an array in-place?
+
+
+Click to reveal answer
+
+**Answer:** Use the `reverse()` method: `arr.reverse()`, or swap elements from both ends:
+```python
+left, right = 0, len(arr) - 1
+while left < right:
+ arr[left], arr[right] = arr[right], arr[left]
+ left += 1
+ right -= 1
+```
+
+**Explanation:** The `reverse()` method is simplest. The manual approach swaps elements from opposite ends moving toward the center.
+
+
+**Question 3:** How would you remove all occurrences of a specific value from an array?
+
+
+Click to reveal answer
+
+**Answer:**
+```python
+value_to_remove = 20
+while value_to_remove in arr:
+ arr.remove(value_to_remove)
+```
+
+**Explanation:** Since `remove()` only removes the first occurrence, you need a loop to remove all occurrences. The loop continues as long as the value exists in the array.
+
+
+**Question 4:** What's the time complexity of searching for an element in an unsorted array?
+
+
+Click to reveal answer
+
+**Answer:** O(n) - Linear time
+
+**Explanation:** In the worst case, you might need to check every element in the array to find the target or determine it's not there. This requires n comparisons for an array of length n.
+
+
+**Question 5:** Why might you choose an array over a list in Python?
+
+
+Click to reveal answer
+
+**Answer:** Arrays are more memory-efficient and faster for large amounts of numeric data of the same type
+
+**Explanation:** Arrays:
+- Store elements more compactly in memory
+- Provide faster operations for numerical computations
+- Are ideal for working with large datasets of homogeneous data
+- Interface well with libraries like NumPy for scientific computing
+
+Lists are better when you need mixed data types or more flexibility.
+
+
+---
+
## Conclusion
Python Arrays are useful when you need to store large amounts of **numeric data** of the same type efficiently.
-They provide faster performance and smaller memory footprint compared to lists for numerical operations.
+They provide faster performance and smaller memory footprint compared to lists for numerical operations.
\ No newline at end of file
diff --git a/docs/python/python-casting.md b/docs/python/python-casting.md
index a5ae390a..6feb331d 100644
--- a/docs/python/python-casting.md
+++ b/docs/python/python-casting.md
@@ -31,8 +31,46 @@ You can specify the data type using casting functions:
x = int(1) # x will be 1
y = int(2.8) # y will be 2
z = int("3") # z will be 3
-````
+```
+
+---
+
+### 🧠 Quiz 1: Casting Basics
+
+**Question 1:** What is type casting in Python?
+
+
+Click to reveal answer
+
+**Answer:** The process of converting a variable from one data type to another
+
+**Explanation:** Type casting allows you to change data types explicitly. For example, converting a string "5" to an integer 5, or a float 3.14 to an integer 3.
+
+
+**Question 2:** What will be the value and type of `x` after this code?
+```python
+x = int(7.9)
+```
+
+
+Click to reveal answer
+
+**Answer:** Value: `7`, Type: `int`
+
+**Explanation:** `int()` converts the float 7.9 to an integer by truncating (cutting off) the decimal part, not rounding. So 7.9 becomes 7.
+
+
+**Question 3:** Is Python's type system static or dynamic?
+
+Click to reveal answer
+
+**Answer:** Dynamic
+
+**Explanation:** Python is dynamically typed, meaning you don't need to declare variable types explicitly. The type is determined at runtime. However, you can use casting to explicitly convert types when needed.
+
+
+---
### `int()` - Integer Casting
@@ -45,6 +83,45 @@ z = int("3") # 3
# w = int("abc") # Error
```
+---
+
+### 🧠 Quiz 2: Integer Casting
+
+**Question 1:** What will `int(9.9)` return?
+
+
+Click to reveal answer
+
+**Answer:** `9`
+
+**Explanation:** `int()` truncates (removes) the decimal part, it doesn't round. So 9.9 becomes 9, not 10. This is different from the `round()` function which would give 10.
+
+
+**Question 2:** Which of the following will cause an error?
+- A) `int(5.5)`
+- B) `int("42")`
+- C) `int("3.14")`
+- D) `int(100)`
+
+
+Click to reveal answer
+
+**Answer:** C) `int("3.14")`
+
+**Explanation:** `int()` can convert numeric strings, but only if they represent whole numbers. "42" works, but "3.14" contains a decimal point and will raise a `ValueError`. You'd need to do `int(float("3.14"))` first.
+
+
+**Question 3:** What is the result of `int(-7.8)`?
+
+
+Click to reveal answer
+
+**Answer:** `-7`
+
+**Explanation:** For negative numbers, `int()` truncates toward zero. So -7.8 becomes -7 (not -8). The decimal part is simply removed.
+
+
+---
### `float()` - Floating-Point Casting
@@ -56,6 +133,51 @@ b = float("2.5") # 2.5
c = float(3.0) # 3.0
```
+---
+
+### 🧠 Quiz 3: Float Casting
+
+**Question 1:** What will `float("10")` return?
+
+
+Click to reveal answer
+
+**Answer:** `10.0`
+
+**Explanation:** `float()` converts the string "10" to a floating-point number 10.0. Even though the input doesn't have a decimal, the output will be a float type.
+
+
+**Question 2:** Which of these is a valid float conversion?
+- A) `float("3.14")`
+- B) `float("100")`
+- C) `float(5)`
+- D) All of the above
+
+
+Click to reveal answer
+
+**Answer:** D) All of the above
+
+**Explanation:** `float()` is very flexible. It can convert:
+- Float strings: `float("3.14")` → 10.0
+- Integer strings: `float("100")` → 100.0
+- Integers: `float(5)` → 5.0
+
+
+**Question 3:** What happens when you do `float(int(3.9))`?
+
+
+Click to reveal answer
+
+**Answer:** `3.0`
+
+**Explanation:**
+1. First, `int(3.9)` converts to `3` (truncates decimal)
+2. Then, `float(3)` converts to `3.0`
+The decimal part (.9) is lost in the first conversion and cannot be recovered.
+
+
+---
### `str()` - String Casting
@@ -67,6 +189,47 @@ y = str(2) # '2'
z = str(3.0) # '3.0'
```
+---
+
+### 🧠 Quiz 4: String Casting
+
+**Question 1:** What is the result of `str(100) + str(200)`?
+
+
+Click to reveal answer
+
+**Answer:** `"100200"` (string concatenation, not addition)
+
+**Explanation:** When you convert numbers to strings and use `+`, it concatenates them as strings rather than adding them mathematically. So `"100" + "200"` = `"100200"`, not `300`.
+
+
+**Question 2:** What will this code print?
+```python
+age = 25
+message = "I am " + str(age) + " years old"
+print(message)
+```
+
+
+Click to reveal answer
+
+**Answer:** `I am 25 years old`
+
+**Explanation:** You must convert the integer `age` to a string using `str()` before concatenating it with other strings. Without `str()`, you'd get a `TypeError`.
+
+
+**Question 3:** What is `str(True)`?
+
+
+Click to reveal answer
+
+**Answer:** `"True"` (string)
+
+**Explanation:** `str()` can convert boolean values to strings. `True` becomes the string `"True"` and `False` becomes `"False"`.
+
+
+---
+
### Invalid Casting
Some values can't be casted directly:
@@ -86,6 +249,8 @@ except ValueError:
print("Invalid conversion")
```
+---
+
### Summary Table
| Function | Converts to | Example Input | Output |
@@ -100,3 +265,109 @@ except ValueError:
* Use casting to convert types manually.
* Useful when handling user input, math, or data from files.
* Always validate input before casting to avoid errors.
+
+---
+
+### 🧠 Quiz 5: Error Handling and Practical Applications
+
+**Question 1:** What error will `int("12.5")` raise?
+
+
+Click to reveal answer
+
+**Answer:** `ValueError`
+
+**Explanation:** `int()` cannot directly convert strings containing decimal points. You must first convert to float: `int(float("12.5"))` which gives `12`.
+
+
+**Question 2:** How can you safely convert user input to an integer?
+
+
+Click to reveal answer
+
+**Answer:** Use try/except block:
+```python
+try:
+ num = int(input("Enter a number: "))
+except ValueError:
+ print("Invalid input!")
+```
+
+**Explanation:** User input can be anything, so wrapping the conversion in try/except prevents your program from crashing when users enter non-numeric data.
+
+
+**Question 3:** What will this code output?
+```python
+x = "5"
+y = "3"
+result = int(x) + int(y)
+print(result)
+```
+
+
+Click to reveal answer
+
+**Answer:** `8`
+
+**Explanation:** Both strings are converted to integers first (5 and 3), then mathematical addition is performed: 5 + 3 = 8. Without casting, `"5" + "3"` would give `"53"` (string concatenation).
+
+
+**Question 4:** Which conversion will work without error?
+- A) `int("Python")`
+- B) `float("3.14.15")`
+- C) `str(None)`
+- D) `int("10.0")`
+
+
+Click to reveal answer
+
+**Answer:** C) `str(None)`
+
+**Explanation:**
+- A) `int("Python")` - ValueError (non-numeric string)
+- B) `float("3.14.15")` - ValueError (invalid format)
+- C) `str(None)` - Works! Returns `"None"`
+- D) `int("10.0")` - ValueError (decimal point in string)
+
+
+**Question 5:** What's the output?
+```python
+price = 19.99
+quantity = 3
+total = str(price * quantity)
+print("Total: $" + total)
+```
+
+
+Click to reveal answer
+
+**Answer:** `Total: $59.97`
+
+**Explanation:**
+1. `price * quantity` = 19.99 × 3 = 59.97 (float)
+2. `str(59.97)` = `"59.97"` (string)
+3. String concatenation: `"Total: $" + "59.97"` = `"Total: $59.97"`
+
+
+**Question 6:** Why is type casting important when working with user input?
+
+
+Click to reveal answer
+
+**Answer:** User input from `input()` is always a string, so you need to cast it to the appropriate type for calculations or comparisons
+
+**Explanation:** Example:
+```python
+age = input("Enter age: ") # "25" (string)
+# age + 5 # Error! Can't add string and int
+age = int(age) # 25 (integer)
+age + 5 # Works! Returns 30
+```
+Without casting, you can't perform mathematical operations on user input.
+
+
+---
+
+## Conclusion
+
+Type casting is an essential skill in Python programming. Understanding when and how to convert between data types helps you handle user input, perform calculations, and avoid type-related errors. Always validate your data before casting to ensure your programs run smoothly!
\ No newline at end of file
diff --git a/docs/python/python-dictionaries.md b/docs/python/python-dictionaries.md
index 07c84ab8..4bdeb456 100644
--- a/docs/python/python-dictionaries.md
+++ b/docs/python/python-dictionaries.md
@@ -37,7 +37,7 @@ person = {
"age": 25,
"city": "New York"
}
-````
+```
## Properties of Dictionaries
@@ -47,6 +47,49 @@ person = {
* Dictionaries are mutable and can be changed after creation.
* In Python 3.7+, dictionaries maintain insertion order.
+---
+
+### 🧠 Quiz 1: Dictionary Basics
+
+**Question 1:** What is the main characteristic that distinguishes a dictionary from a list?
+
+
+Click to reveal answer
+
+**Answer:** Dictionaries store data as key-value pairs, while lists store elements by index
+
+**Explanation:** Lists use numeric indices (0, 1, 2...) to access elements, while dictionaries use keys (which can be strings, numbers, etc.) to access values. This makes dictionaries ideal for structured data where you want meaningful names for your data.
+
+
+**Question 2:** Which of the following can be used as a dictionary key?
+- A) `"name"` (string)
+- B) `42` (integer)
+- C) `(1, 2)` (tuple)
+- D) `[1, 2]` (list)
+
+
+Click to reveal answer
+
+**Answer:** A, B, and C (string, integer, and tuple) - NOT D
+
+**Explanation:** Dictionary keys must be immutable (unchangeable). Strings, integers, and tuples are immutable and can be keys. Lists are mutable, so they cannot be used as keys. Attempting to use a list as a key will raise a `TypeError`.
+
+
+**Question 3:** What happens if you use the same key twice when creating a dictionary?
+```python
+data = {"x": 10, "y": 20, "x": 30}
+```
+
+
+Click to reveal answer
+
+**Answer:** The second value overwrites the first; `data` becomes `{"x": 30, "y": 20}`
+
+**Explanation:** Dictionary keys must be unique. If you assign a value to an existing key, it overwrites the old value. The dictionary will only contain one "x" key with the latest value (30).
+
+
+---
+
## Creating Dictionaries
### Using Curly Braces:
@@ -82,6 +125,60 @@ person.get("age")
person.get("gender", "Not Found")
```
+---
+
+### 🧠 Quiz 2: Accessing Dictionary Elements
+
+**Question 1:** What's the difference between `person["age"]` and `person.get("age")`?
+
+
+Click to reveal answer
+
+**Answer:** `person["age"]` raises a `KeyError` if the key doesn't exist, while `person.get("age")` returns `None`
+
+**Explanation:**
+```python
+person = {"name": "Alice"}
+person["age"] # KeyError: 'age'
+person.get("age") # Returns None (no error)
+person.get("age", 0) # Returns 0 (default value)
+```
+Using `.get()` is safer when you're not sure if a key exists.
+
+
+**Question 2:** What will be the output?
+```python
+student = {"name": "Bob", "grade": "A"}
+print(student.get("score", 100))
+```
+
+
+Click to reveal answer
+
+**Answer:** `100`
+
+**Explanation:** The key "score" doesn't exist in the dictionary. The `.get()` method returns the default value (100) when the key is not found. This is useful for providing fallback values.
+
+
+**Question 3:** How do you check if a key exists in a dictionary?
+
+
+Click to reveal answer
+
+**Answer:** Use the `in` operator: `if "key" in dictionary:`
+
+**Explanation:**
+```python
+person = {"name": "Alice", "age": 25}
+if "name" in person:
+ print("Name exists") # This will execute
+if "email" in person:
+ print("Email exists") # This won't execute
+```
+
+
+---
+
## Adding and Updating Items
### Add New Key-Value:
@@ -102,6 +199,61 @@ person["age"] = 30
person.update({"age": 35, "city": "Chicago"})
```
+---
+
+### 🧠 Quiz 3: Modifying Dictionaries
+
+**Question 1:** What will the dictionary look like after this code?
+```python
+data = {"a": 1, "b": 2}
+data["c"] = 3
+data["a"] = 10
+```
+
+
+Click to reveal answer
+
+**Answer:** `{"a": 10, "b": 2, "c": 3}`
+
+**Explanation:**
+- `data["c"] = 3` adds a new key "c" with value 3
+- `data["a"] = 10` updates the existing key "a" from 1 to 10
+
+
+**Question 2:** What does the `update()` method do if a key already exists?
+
+
+Click to reveal answer
+
+**Answer:** It overwrites the existing value with the new value
+
+**Explanation:**
+```python
+person = {"name": "Alice", "age": 25}
+person.update({"age": 30, "city": "Boston"})
+# Result: {"name": "Alice", "age": 30, "city": "Boston"}
+```
+The "age" is updated to 30, and "city" is added as a new key.
+
+
+**Question 3:** Can you add multiple key-value pairs at once?
+
+
+Click to reveal answer
+
+**Answer:** Yes, using the `update()` method with another dictionary
+
+**Explanation:**
+```python
+data = {"x": 1}
+data.update({"y": 2, "z": 3})
+# Result: {"x": 1, "y": 2, "z": 3}
+```
+This is more efficient than adding keys one by one.
+
+
+---
+
## Removing Elements
### Using `pop()`:
@@ -130,6 +282,75 @@ Removes and returns the last inserted key-value pair.
person.popitem()
```
+---
+
+### 🧠 Quiz 4: Removing Elements
+
+**Question 1:** What's the difference between `pop()` and `del`?
+
+
+Click to reveal answer
+
+**Answer:** `pop()` removes the key and returns its value, while `del` just removes the key without returning anything
+
+**Explanation:**
+```python
+person = {"name": "Alice", "age": 25}
+age = person.pop("age") # age = 25, person = {"name": "Alice"}
+del person["name"] # person = {}, nothing is returned
+```
+
+
+**Question 2:** What does `popitem()` return?
+
+
+Click to reveal answer
+
+**Answer:** A tuple containing the key-value pair that was removed (the last inserted item)
+
+**Explanation:**
+```python
+person = {"name": "Alice", "age": 25, "city": "NYC"}
+item = person.popitem() # Returns ("city", "NYC")
+# person is now {"name": "Alice", "age": 25}
+```
+In Python 3.7+, it removes the last inserted item in order.
+
+
+**Question 3:** What happens after calling `clear()` on a dictionary?
+
+
+Click to reveal answer
+
+**Answer:** The dictionary becomes empty `{}`
+
+**Explanation:**
+```python
+data = {"a": 1, "b": 2, "c": 3}
+data.clear()
+print(data) # {}
+```
+`clear()` removes all items from the dictionary, leaving an empty dictionary (not `None`).
+
+
+**Question 4:** What happens if you try to `pop()` a key that doesn't exist?
+
+
+Click to reveal answer
+
+**Answer:** It raises a `KeyError` unless you provide a default value
+
+**Explanation:**
+```python
+person = {"name": "Alice"}
+person.pop("age") # KeyError
+person.pop("age", None) # Returns None (no error)
+person.pop("age", "N/A") # Returns "N/A" (no error)
+```
+
+
+---
+
## Dictionary Methods
| Method | Description |
@@ -167,6 +388,79 @@ for key, value in person.items():
print(key, value)
```
+---
+
+### 🧠 Quiz 5: Dictionary Methods and Iteration
+
+**Question 1:** What will this code print?
+```python
+data = {"a": 1, "b": 2, "c": 3}
+print(list(data.keys()))
+```
+
+
+Click to reveal answer
+
+**Answer:** `['a', 'b', 'c']`
+
+**Explanation:** The `.keys()` method returns a view of all the keys in the dictionary. Converting it to a list gives you a list of the keys.
+
+
+**Question 2:** How do you iterate through both keys and values simultaneously?
+
+
+Click to reveal answer
+
+**Answer:** Use `.items()` with tuple unpacking in a for loop
+
+**Explanation:**
+```python
+person = {"name": "Alice", "age": 25}
+for key, value in person.items():
+ print(f"{key}: {value}")
+# Output:
+# name: Alice
+# age: 25
+```
+
+
+**Question 3:** What's the difference between `copy()` and direct assignment?
+
+
+Click to reveal answer
+
+**Answer:** `copy()` creates a new dictionary, while direct assignment creates a reference to the same dictionary
+
+**Explanation:**
+```python
+original = {"a": 1}
+reference = original # Same object
+copy = original.copy() # New object
+
+reference["b"] = 2
+print(original) # {"a": 1, "b": 2} - Changed!
+
+copy["c"] = 3
+print(original) # {"a": 1, "b": 2} - Unchanged
+```
+
+
+**Question 4:** What will this output?
+```python
+scores = {"math": 90, "science": 85, "english": 92}
+print(max(scores.values()))
+```
+
+
+Click to reveal answer
+
+**Answer:** `92`
+
+**Explanation:** `scores.values()` returns all the values (90, 85, 92), and `max()` finds the highest value, which is 92.
+
+
+---
+
## Nested Dictionaries
A dictionary can contain other dictionaries as values, enabling hierarchical data storage.
@@ -187,6 +481,76 @@ Like list comprehensions, dictionary comprehensions offer a concise way to creat
squares = {x: x*x for x in range(1, 6)}
```
+---
+
+### 🧠 Quiz 6: Advanced Dictionary Concepts
+
+**Question 1:** How do you access a nested dictionary value?
+```python
+data = {"user": {"profile": {"name": "Alice"}}}
+```
+
+
+Click to reveal answer
+
+**Answer:** Chain the keys: `data["user"]["profile"]["name"]`
+
+**Explanation:** Access each level one at a time using consecutive square brackets. This returns "Alice".
+
+
+**Question 2:** What will this dictionary comprehension create?
+```python
+result = {x: x**2 for x in range(1, 4)}
+```
+
+
+Click to reveal answer
+
+**Answer:** `{1: 1, 2: 4, 3: 9}`
+
+**Explanation:** The comprehension creates key-value pairs where each number from 1 to 3 is the key, and its square is the value.
+
+
+**Question 3:** How can you filter a dictionary comprehension?
+
+
+Click to reveal answer
+
+**Answer:** Add an `if` condition at the end
+
+**Explanation:**
+```python
+numbers = {"a": 1, "b": 2, "c": 3, "d": 4}
+evens = {k: v for k, v in numbers.items() if v % 2 == 0}
+# Result: {"b": 2, "d": 4}
+```
+This creates a new dictionary with only even values.
+
+
+**Question 4:** What's a practical use case for nested dictionaries?
+
+
+Click to reveal answer
+
+**Answer:** Representing structured data like JSON, database records, or hierarchical information
+
+**Explanation:** Example - storing student information:
+```python
+school = {
+ "Class A": {
+ "student1": {"name": "John", "age": 15},
+ "student2": {"name": "Emma", "age": 16}
+ },
+ "Class B": {
+ "student3": {"name": "Mike", "age": 15}
+ }
+}
+```
+This naturally represents hierarchical relationships.
+
+
+---
+
## Use Cases of Dictionaries
* Representing JSON or structured data
@@ -212,9 +576,96 @@ squares = {x: x*x for x in range(1, 6)}
* Use keys that are hashable (e.g., strings, numbers).
* Use dictionaries for fast lookups and structured data representation.
+---
+
+### 🧠 Quiz 7: Best Practices and Use Cases
+
+**Question 1:** Why is using `.get()` considered a best practice?
+
+
+Click to reveal answer
+
+**Answer:** It prevents `KeyError` exceptions and allows you to provide default values
+
+**Explanation:** Instead of:
+```python
+# Risky - might crash
+value = person["email"]
+```
+Use:
+```python
+# Safe - returns None or default if key missing
+value = person.get("email", "no-email@example.com")
+```
+
+
+**Question 2:** When should you use a dictionary instead of a list?
+
+
+Click to reveal answer
+
+**Answer:** When you need to access data by meaningful keys rather than numeric indices, or when you need fast lookups
+
+**Explanation:**
+- Use **Dictionary**: Storing user profiles, configuration settings, word frequencies
+- Use **List**: Ordered sequences, when numeric position matters, collections of similar items
+
+
+**Question 3:** What's a common use case for counting word frequencies?
+
+
+Click to reveal answer
+
+**Answer:**
+```python
+text = "hello world hello"
+word_count = {}
+for word in text.split():
+ word_count[word] = word_count.get(word, 0) + 1
+# Result: {"hello": 2, "world": 1}
+```
+
+**Explanation:** Dictionaries are perfect for counting because you can use the word as the key and increment its count. The `.get()` method provides a default of 0 for new words.
+
+
+**Question 4:** Why must dictionary keys be hashable?
+
+
+Click to reveal answer
+
+**Answer:** Because Python uses hashing to store and quickly retrieve values by their keys
+
+**Explanation:** Hashable objects (strings, numbers, tuples) have a fixed hash value that allows Python to quickly locate them in memory. This gives dictionaries O(1) lookup time. Mutable objects like lists can't be hashed because their content can change.
+
+
+**Question 5:** Which is more efficient for lookup operations?
+```python
+# Option A: List
+names = ["Alice", "Bob", "Charlie"]
+"Bob" in names
+
+# Option B: Dictionary
+names = {"Alice": 1, "Bob": 2, "Charlie": 3}
+"Bob" in names
+```
+
+
+Click to reveal answer
+
+**Answer:** Option B (Dictionary) - O(1) average time complexity
+
+**Explanation:**
+- **List lookup**: O(n) - must check each element until found
+- **Dictionary lookup**: O(1) - uses hashing for instant access
+
+For 1000 items, a list might need 1000 checks, while a dictionary typically needs just 1!
+
+
+---
+
## Summary
* Dictionaries are one of the most versatile data structures in Python.
* They store key-value pairs and allow fast retrieval based on keys.
* Keys must be unique and immutable.
-* Dictionaries support powerful methods for data manipulation and traversal.
+* Dictionaries support powerful methods for data manipulation and traversal.
\ No newline at end of file
diff --git a/docs/python/python-errors-and-exceptions.md b/docs/python/python-errors-and-exceptions.md
index 7e9bfbfa..3c5a7471 100644
--- a/docs/python/python-errors-and-exceptions.md
+++ b/docs/python/python-errors-and-exceptions.md
@@ -75,6 +75,47 @@ except ExceptionType:
---
+### Quiz 1: Exception Handling Basics
+
+**Question 1:** What happens if an exception is not caught in a try-except block?
+
+
+Click to reveal answer
+
+**Answer:** The program crashes and displays an error traceback
+
+**Explanation:** Without a try-except block, Python will terminate the program and print a traceback showing where the error occurred. Exception handling prevents this crash and allows the program to continue running.
+
+
+**Question 2:** What will this code output?
+```python
+try:
+ x = 10 / 2
+ print(x)
+except ZeroDivisionError:
+ print("Error!")
+```
+
+
+Click to reveal answer
+
+**Answer:** `5.0`
+
+**Explanation:** Since no exception occurs (10/2 is valid), the try block executes normally and prints 5.0. The except block is never executed.
+
+
+**Question 3:** Why should you catch specific exceptions rather than using a bare `except:`?
+
+
+Click to reveal answer
+
+**Answer:** To avoid accidentally catching and hiding unexpected errors
+
+**Explanation:** Catching specific exceptions (like `ValueError` or `ZeroDivisionError`) helps you handle known issues while allowing unexpected errors to surface. A bare `except:` catches everything, including system exits and keyboard interrupts, which can mask serious bugs.
+
+
+---
+
## Catching Specific Exceptions
### Single Exception
@@ -134,6 +175,54 @@ except TypeError:
---
+### Quiz 2: Specific Exceptions
+
+**Question 1:** Which exception is raised by this code?
+```python
+my_list = [1, 2, 3]
+print(my_list[5])
+```
+
+
+Click to reveal answer
+
+**Answer:** `IndexError`
+
+**Explanation:** Trying to access an index that doesn't exist in a list raises an `IndexError`. The list has indices 0, 1, and 2, but we're trying to access index 5.
+
+
+**Question 2:** What exception occurs here?
+```python
+person = {"name": "Bob"}
+print(person["email"])
+```
+
+
+Click to reveal answer
+
+**Answer:** `KeyError`
+
+**Explanation:** Accessing a dictionary key that doesn't exist raises a `KeyError`. The dictionary only has a "name" key, not an "email" key. Use `.get()` to avoid this error.
+
+
+**Question 3:** Match the error to the code:
+- A) `int("3.14")`
+- B) `10 / 0`
+- C) `"text" * "2"`
+
+
+Click to reveal answer
+
+**Answer:**
+- A) `ValueError` - can't convert "3.14" directly to int
+- B) `ZeroDivisionError` - division by zero
+- C) `TypeError` - can't multiply two strings
+
+**Explanation:** Each operation fails for a different reason, resulting in a specific exception type that describes the problem.
+
+
+---
+
## Multiple Exception Handlers
Handle different exceptions separately:
@@ -176,6 +265,77 @@ except (ValueError, ZeroDivisionError) as e:
---
+### Quiz 3: Multiple Exception Handling
+
+**Question 1:** What's the advantage of handling exceptions separately vs. together?
+
+
+Click to reveal answer
+
+**Answer:** Separate handlers allow you to provide specific error messages and take different actions for each error type
+
+**Explanation:**
+```python
+# Separate - more specific feedback
+except ValueError:
+ print("Please enter a valid number")
+except ZeroDivisionError:
+ print("Cannot divide by zero")
+
+# Together - same handling for both
+except (ValueError, ZeroDivisionError):
+ print("Invalid input")
+```
+Use separate handlers when you need different responses for different errors.
+
+
+**Question 2:** What's the purpose of the `as` keyword in exception handling?
+
+
+Click to reveal answer
+
+**Answer:** It captures the exception object so you can access its details and message
+
+**Explanation:**
+```python
+try:
+ x = int("abc")
+except ValueError as e:
+ print(f"Error details: {e}")
+ # Can now access the exception message and other attributes
+```
+This is useful for logging errors or providing detailed feedback to users.
+
+
+**Question 3:** In what order should exception handlers be placed?
+
+
+Click to reveal answer
+
+**Answer:** Most specific exceptions first, then more general ones
+
+**Explanation:**
+```python
+# Correct order
+try:
+ risky_operation()
+except ValueError: # Specific
+ handle_value_error()
+except Exception: # General
+ handle_other_errors()
+
+# Wrong - Exception catches everything, ValueError never reached
+try:
+ risky_operation()
+except Exception:
+ handle_other_errors()
+except ValueError: # This will never execute!
+ handle_value_error()
+```
+
+
+---
+
## The `else` Clause
The `else` block executes **only if no exception occurs** in the `try` block:
@@ -251,6 +411,92 @@ process_file("numbers.txt")
---
+### Quiz 4: else and finally Clauses
+
+**Question 1:** When does the `else` block execute?
+
+
+Click to reveal answer
+
+**Answer:** Only when no exception occurs in the try block
+
+**Explanation:**
+```python
+try:
+ x = 10 / 2
+except ZeroDivisionError:
+ print("Error")
+else:
+ print("Success!") # This runs because no error occurred
+```
+The `else` block is for code that should only run when everything succeeds.
+
+
+**Question 2:** What's the key difference between `else` and `finally`?
+
+
+Click to reveal answer
+
+**Answer:** `else` runs only if no exception occurred; `finally` always runs regardless of exceptions
+
+**Explanation:**
+- `else`: Executes only on success (no exceptions)
+- `finally`: Always executes (success or failure)
+
+`finally` is perfect for cleanup operations like closing files or database connections that must happen regardless of errors.
+
+
+**Question 3:** What will this code output?
+```python
+try:
+ result = 10 / 0
+except ZeroDivisionError:
+ print("Error caught")
+else:
+ print("No error")
+finally:
+ print("Cleanup")
+```
+
+
+Click to reveal answer
+
+**Answer:**
+```
+Error caught
+Cleanup
+```
+
+**Explanation:**
+1. An error occurs, so the except block prints "Error caught"
+2. The else block is skipped (only runs if no error)
+3. The finally block always runs, printing "Cleanup"
+
+
+**Question 4:** Why is `finally` useful for resource management?
+
+
+Click to reveal answer
+
+**Answer:** It guarantees cleanup code runs even if an exception occurs or the function returns early
+
+**Explanation:**
+```python
+file = None
+try:
+ file = open("data.txt")
+ # If error occurs here, file still gets closed
+ process(file)
+ return result # Even with early return
+finally:
+ if file:
+ file.close() # This always runs
+```
+This prevents resource leaks from unclosed files, connections, etc.
+
+
+---
+
## Complete `try...except...else...finally` Structure
All clauses together:
@@ -365,6 +611,61 @@ except ValueError as e:
---
+### Quiz 5: Raising Exceptions
+
+**Question 1:** When should you raise an exception in your code?
+
+
+Click to reveal answer
+
+**Answer:** When invalid input or an abnormal condition is detected that the function cannot handle properly
+
+**Explanation:** Raising exceptions is appropriate for signaling errors like:
+- Invalid function arguments (negative age, empty required fields)
+- Failed business logic rules (insufficient funds, duplicate entries)
+- Precondition violations
+
+Don't use exceptions for normal program flow (like reaching the end of a loop).
+
+
+**Question 2:** What does the bare `raise` keyword do (without specifying an exception)?
+
+
+Click to reveal answer
+
+**Answer:** It re-raises the current exception that was just caught
+
+**Explanation:**
+```python
+try:
+ int("abc")
+except ValueError:
+ print("Logging the error...")
+ raise # Re-raises the ValueError
+```
+This is useful when you want to log an error but still let it propagate up to be handled elsewhere.
+
+
+**Question 3:** What will happen?
+```python
+def validate_score(score):
+ if score < 0 or score > 100:
+ raise ValueError("Score must be between 0 and 100")
+ return score
+
+result = validate_score(150)
+```
+
+
+Click to reveal answer
+
+**Answer:** The program will crash with a `ValueError` (unless caught)
+
+**Explanation:** Since the score 150 is invalid, the function raises a `ValueError`. Without a try-except block to catch it, this will terminate the program and display an error traceback.
+
+
+---
+
## Custom Exceptions
Create your own exception classes:
@@ -412,6 +713,71 @@ except AgeRestrictionError as e:
---
+### Quiz 6: Custom Exceptions
+
+**Question 1:** Why create custom exception classes?
+
+
+Click to reveal answer
+
+**Answer:** To create more meaningful, domain-specific errors that are easier to catch and handle appropriately
+
+**Explanation:** Custom exceptions like `InsufficientFundsError` or `InvalidEmailError` are more descriptive than generic `ValueError`. They allow you to:
+- Catch specific business logic errors
+- Add custom attributes (like `minimum_balance`)
+- Provide clearer error messages
+- Organize exception handling by problem domain
+
+
+**Question 2:** What must custom exception classes inherit from?
+
+
+Click to reveal answer
+
+**Answer:** The `Exception` class (or another exception class)
+
+**Explanation:**
+```python
+# Correct
+class MyError(Exception):
+ pass
+
+# Also correct - inherit from more specific exception
+class MyValueError(ValueError):
+ pass
+
+# Wrong - doesn't inherit from Exception
+class MyError:
+ pass
+```
+All exceptions must be part of the exception hierarchy.
+
+
+**Question 3:** How do you add custom attributes to an exception?
+
+
+Click to reveal answer
+
+**Answer:** Define an `__init__` method that stores the attributes and calls `super().__init__()`
+
+**Explanation:**
+```python
+class PaymentError(Exception):
+ def __init__(self, amount, balance):
+ self.amount = amount
+ self.balance = balance
+ self.shortage = amount - balance
+ super().__init__(f"Insufficient funds: need ${self.shortage} more")
+
+try:
+ raise PaymentError(100, 75)
+except PaymentError as e:
+ print(e.shortage) # Access custom attribute: 25
+```
+
+
+---
+
## Exception Hierarchy
Python exceptions follow a hierarchy. Catching a parent exception catches all child exceptions:
@@ -457,6 +823,91 @@ except LookupError as e: # Parent of IndexError
---
+### Quiz 7: Exception Hierarchy and Best Practices
+
+**Question 1:** Why is catching `Exception` generally discouraged?
+
+
+Click to reveal answer
+
+**Answer:** It catches too many different types of errors, making it hard to handle each appropriately
+
+**Explanation:** `except Exception:` catches almost all exceptions, including ones you might not expect. This can hide bugs and make debugging difficult. Better to catch specific exceptions you know how to handle.
+
+
+**Question 2:** What's the difference between `BaseException` and `Exception`?
+
+
+Click to reveal answer
+
+**Answer:** `BaseException` includes system-level exceptions like `KeyboardInterrupt` and `SystemExit`, while `Exception` is for catchable program errors
+
+**Explanation:**
+- Catch `Exception`: Normal error handling
+- Avoid catching `BaseException`: Includes system exits and Ctrl+C interrupts that shouldn't be caught
+
+```python
+# Bad - prevents Ctrl+C from working
+try:
+ long_running_task()
+except BaseException: # Don't do this!
+ pass
+```
+
+
+**Question 3:** What happens if you catch a parent exception before a child exception?
+
+
+Click to reveal answer
+
+**Answer:** The child exception handler becomes unreachable and will never execute
+
+**Explanation:**
+```python
+# Wrong order
+try:
+ code()
+except Exception: # Catches everything
+ print("General error")
+except ValueError: # Never reached!
+ print("Value error")
+
+# Correct order
+try:
+ code()
+except ValueError: # Specific first
+ print("Value error")
+except Exception: # General last
+ print("General error")
+```
+
+
+**Question 4:** Which best practice is demonstrated here?
+```python
+try:
+ process_payment(amount)
+except InsufficientFundsError as e:
+ notify_user(f"Payment failed: {e}")
+ log_error(e)
+except PaymentGatewayError as e:
+ notify_user("Payment system unavailable")
+ retry_payment(amount)
+```
+
+
+Click to reveal answer
+
+**Answer:** Catching specific exceptions and handling each differently based on the type of error
+
+**Explanation:** This code demonstrates good exception handling by:
+- Using specific, meaningful exception types
+- Providing different handling for different errors
+- Taking appropriate action for each case (notify vs. retry)
+- Maintaining good user experience during errors
+
+
+---
+
## Practical Examples
### Example 1: Safe User Input
diff --git a/docs/python/python-functions.md b/docs/python/python-functions.md
index a15ccd8b..4e953cf1 100644
--- a/docs/python/python-functions.md
+++ b/docs/python/python-functions.md
@@ -114,6 +114,42 @@ print(result) # Output: None
---
+## 🎯 Quiz 1: Basic Functions & Return Statements
+
+**Question 1:** What keyword is used to define a function in Python?
+
+Show Answer
+
+`def` - The `def` keyword is used to define functions in Python.
+
+
+**Question 2:** What will be the output of the following code?
+```python
+def calculate(x, y):
+ return x + y
+ return x * y
+
+result = calculate(3, 4)
+print(result)
+```
+
+
+Show Answer
+
+**Output:** `7`
+
+**Explanation:** The function returns `7` (3 + 4) and exits immediately. The second return statement is never executed because the function stops executing after the first `return`.
+
+
+**Question 3:** If a function doesn't have a `return` statement, what does it return?
+
+Show Answer
+
+`None` - Functions without a return statement automatically return `None`.
+
+
+---
+
## Default Arguments
You can provide default values for parameters:
@@ -159,6 +195,49 @@ book_info("Hamlet", author="Shakespeare", year=1600)
---
+## 🎯 Quiz 2: Default & Keyword Arguments
+
+**Question 1:** What will be the output?
+```python
+def greet(name, message="Hello"):
+ return f"{message}, {name}!"
+
+print(greet("Alice"))
+print(greet("Bob", "Hi"))
+```
+
+
+Show Answer
+
+**Output:**
+```
+Hello, Alice!
+Hi, Bob!
+```
+
+**Explanation:** The first call uses the default value "Hello" for `message`, while the second call overrides it with "Hi".
+
+
+**Question 2:** Is this function call valid?
+```python
+def display(a, b, c):
+ print(a, b, c)
+
+display(c=3, a=1, b=2)
+```
+
+
+Show Answer
+
+**Yes, it's valid!**
+
+**Output:** `1 2 3`
+
+**Explanation:** When using keyword arguments, you can pass them in any order. Python matches them by parameter name.
+
+
+---
+
## Variable-Length Arguments: `*args`
Use `*args` to accept any number of positional arguments:
@@ -221,6 +300,51 @@ flexible_function(1, 2, 3, name="Alice", age=30)
---
+## 🎯 Quiz 3: *args and **kwargs
+
+**Question 1:** What is `*args` used for?
+
+Show Answer
+
+`*args` allows a function to accept any number of **positional arguments**. The arguments are collected into a tuple inside the function.
+
+
+**Question 2:** What will be the output?
+```python
+def process(*args, **kwargs):
+ print(type(args))
+ print(type(kwargs))
+
+process(1, 2, 3, name="Test", value=100)
+```
+
+
+Show Answer
+
+**Output:**
+```
+
+
+```
+
+**Explanation:** `*args` collects positional arguments into a **tuple**, and `**kwargs` collects keyword arguments into a **dictionary**.
+
+
+**Question 3:** Can you use both `*args` and `**kwargs` in the same function?
+
+Show Answer
+
+**Yes!** You can use both in the same function. Just remember the correct order: regular parameters, `*args`, then `**kwargs`.
+
+Example:
+```python
+def my_function(a, b, *args, **kwargs):
+ pass
+```
+
+
+---
+
## Function Parameter Order
When combining different types of parameters, follow this order:
@@ -342,6 +466,64 @@ print(greet("Alice")) # Output: Hello, Alice!
---
+## 🎯 Quiz 4: Scope & Lambda Functions
+
+**Question 1:** What will happen when you run this code?
+```python
+def test():
+ x = 10
+ print(x)
+
+test()
+print(x)
+```
+
+
+Show Answer
+
+**Error!** The second `print(x)` will raise a `NameError` because `x` is a local variable defined inside the `test()` function. It doesn't exist outside the function.
+
+
+**Question 2:** What's the output?
+```python
+count = 5
+
+def update():
+ global count
+ count = 10
+
+update()
+print(count)
+```
+
+
+Show Answer
+
+**Output:** `10`
+
+**Explanation:** The `global` keyword allows the function to modify the global variable `count`, changing it from 5 to 10.
+
+
+**Question 3:** What does this lambda function do?
+```python
+multiply = lambda x, y: x * y
+result = multiply(4, 5)
+```
+
+
+Show Answer
+
+**Result:** `20`
+
+**Explanation:** This lambda function takes two arguments (`x` and `y`) and returns their product. It's equivalent to:
+```python
+def multiply(x, y):
+ return x * y
+```
+
+
+---
+
## Practical Examples
### Example 1: Temperature Converter
@@ -462,3 +644,119 @@ def greet_user(name: str, times: int = 1) -> None:
| Docstring | Function documentation | `"""Function description"""` |
Functions are fundamental building blocks in Python that make code reusable, organized, and maintainable. Master these concepts to write clean and efficient Python programs!
+
+---
+
+## 🎯 Final Quiz: Comprehensive Review
+
+**Question 1:** What's wrong with this function definition?
+```python
+def my_function(**kwargs, *args):
+ pass
+```
+
+
+Show Answer
+
+**Error!** The parameter order is incorrect. `*args` must come before `**kwargs`.
+
+**Correct order:**
+```python
+def my_function(*args, **kwargs):
+ pass
+```
+
+
+**Question 2:** What will this function return?
+```python
+def mystery(a, b=5, *args, **kwargs):
+ return a + b + sum(args) + sum(kwargs.values())
+
+result = mystery(1, 2, 3, 4, x=5, y=6)
+```
+
+
+Show Answer
+
+**Result:** `21`
+
+**Breakdown:**
+- `a = 1`
+- `b = 2`
+- `args = (3, 4)`, sum = 7
+- `kwargs = {'x': 5, 'y': 6}`, sum = 11
+- Total: 1 + 2 + 7 + 11 = 21
+
+
+**Question 3:** Which best practice is being followed in this function?
+```python
+def calculate_discount(price: float, discount_rate: float = 0.1) -> float:
+ """
+ Calculate the discounted price.
+
+ Args:
+ price: Original price
+ discount_rate: Discount percentage (default 10%)
+
+ Returns:
+ Final price after discount
+ """
+ return price * (1 - discount_rate)
+```
+
+
+Show Answer
+
+**All of them!** This function demonstrates:
+1. ✅ Descriptive function name
+2. ✅ Type hints for parameters and return value
+3. ✅ Comprehensive docstring
+4. ✅ Default argument for optional parameter
+5. ✅ Single, focused responsibility
+
+This is an excellent example of Python function best practices!
+
+
+**Question 4:** Create a function that accepts a name and any number of scores, returns the average score.
+
+
+Show Answer
+
+```python
+def calculate_average(name, *scores):
+ """Calculate the average of given scores."""
+ if not scores:
+ return 0
+
+ average = sum(scores) / len(scores)
+ print(f"{name}'s average: {average:.2f}")
+ return average
+
+# Usage:
+calculate_average("Alice", 85, 90, 78, 92)
+# Output: Alice's average: 86.25
+```
+
+
+**Question 5:** What's the difference between parameters and arguments?
+
+
+Show Answer
+
+- **Parameters:** Variables listed in the function definition
+ ```python
+ def greet(name): # 'name' is a parameter
+ print(f"Hello, {name}")
+ ```
+
+- **Arguments:** Actual values passed to the function when calling it
+ ```python
+ greet("Alice") # "Alice" is an argument
+ ```
+
+**Simple rule:** Parameters are the placeholders, arguments are the actual values!
+
+
+---
+
+🎉 **Congratulations!** You've completed the Python Functions guide with quizzes. Practice writing your own functions to reinforce these concepts!
\ No newline at end of file
diff --git a/docs/python/python-io.md b/docs/python/python-io.md
index 58a58c74..9bbd029d 100644
--- a/docs/python/python-io.md
+++ b/docs/python/python-io.md
@@ -33,80 +33,233 @@ To work with files in Python, you use the built-in `open()` function.
```python
file = open("example.txt", "r") # Open for reading
-````
+```
### Modes:
| Mode | Description |
| ----- | ---------------------------------------------------- |
-| `'r'` | Read (default). Fails if the file doesn’t exist. |
+| `'r'` | Read (default). Fails if the file doesn't exist. |
| `'w'` | Write. Creates a new file or truncates existing one. |
| `'a'` | Append. Adds content to the end of the file. |
| `'b'` | Binary mode. Used with `'rb'`, `'wb'`, etc. |
| `'x'` | Create. Fails if the file already exists. |
+---
+
+## 🎯 Quiz 1: File Opening Modes
+
+**Question 1:** What happens when you open a file with `'w'` mode if the file already exists?
+
+Show Answer
+
+The file is **truncated** (all existing content is deleted) and a new empty file is created. If you want to preserve existing content, use `'a'` (append) mode instead.
+
+
+**Question 2:** What is the default mode when you use `open("file.txt")` without specifying a mode?
+
+Show Answer
+
+**`'r'` (read mode)** is the default. This means if you don't specify a mode, the file will be opened for reading only.
+
+
+**Question 3:** Which mode would you use to add new content to the end of an existing file without deleting its current content?
+
+Show Answer
+
+**`'a'` (append mode)**
+
+Example:
+```python
+with open("log.txt", "a") as file:
+ file.write("New log entry\n")
+```
+This adds content to the end without removing existing data.
+
+
+---
## Reading from a File
### `read()` – Reads entire content
-````python
+```python
with open("example.txt", "r") as file:
content = file.read()
print(content)
-````
+```
### `readline()` – Reads one line at a time
-````python
+```python
with open("example.txt", "r") as file:
line = file.readline()
print(line)
-````
+```
### `readlines()` – Reads all lines into a list
-````python
+```python
with open("example.txt", "r") as file:
lines = file.readlines()
print(lines)
-````
+```
+
+---
+
+## 🎯 Quiz 2: Reading Methods
+
+**Question 1:** What's the difference between `read()` and `readlines()`?
+
+Show Answer
+
+- **`read()`**: Returns the entire file content as a **single string**
+- **`readlines()`**: Returns a **list** where each element is a line from the file (including `\n` characters)
+
+Example:
+```python
+# File content: "Hello\nWorld\n"
+
+# Using read()
+content = file.read() # Returns: "Hello\nWorld\n"
+
+# Using readlines()
+lines = file.readlines() # Returns: ["Hello\n", "World\n"]
+```
+
+
+**Question 2:** If a file has 5 lines and you call `readline()` three times, what happens?
+
+Show Answer
+
+You will read the **first three lines** of the file, one at a time. Each call to `readline()` moves the file pointer to the next line.
+
+```python
+with open("file.txt", "r") as file:
+ line1 = file.readline() # First line
+ line2 = file.readline() # Second line
+ line3 = file.readline() # Third line
+ # Lines 4 and 5 remain unread
+```
+
+
+**Question 3:** What will be the output?
+```python
+with open("test.txt", "r") as file:
+ lines = file.readlines()
+ print(type(lines))
+```
+
+Show Answer
+
+**Output:** ``
+
+**Explanation:** `readlines()` always returns a list containing all lines from the file.
+
+
+---
## Writing to a File
### `write()` – Write string to file
-````python
+```python
with open("output.txt", "w") as file:
file.write("Hello, world!")
-````
+```
### `writelines()` – Write list of strings
-````python
+```python
lines = ["Line 1\n", "Line 2\n"]
with open("output.txt", "w") as file:
file.writelines(lines)
-````
+```
## Using `with` Statement (Best Practice)
The `with` block ensures the file is automatically closed after use:
-````python
+```python
with open("data.txt", "r") as file:
data = file.read()
-````
+```
This is the **recommended way** to handle files in Python.
+---
+
+## 🎯 Quiz 3: Writing Files & Best Practices
+
+**Question 1:** What's the difference between `write()` and `writelines()`?
+
+Show Answer
+
+- **`write(string)`**: Writes a single string to the file
+- **`writelines(list)`**: Writes a list of strings to the file
+
+**Important:** `writelines()` does NOT add newline characters automatically!
+
+```python
+# write()
+file.write("Hello\n") # Adds newline
+
+# writelines()
+file.writelines(["Hello", "World"]) # Writes: "HelloWorld"
+file.writelines(["Hello\n", "World\n"]) # Writes on separate lines
+```
+
+
+**Question 2:** Why is using the `with` statement recommended for file operations?
+
+Show Answer
+
+The `with` statement **automatically closes the file** when the block is exited, even if an error occurs. This prevents:
+- Memory leaks
+- File corruption
+- Resource locking issues
+
+**Without `with`:**
+```python
+file = open("data.txt", "r")
+data = file.read()
+file.close() # Must remember to close!
+```
+
+**With `with` (Better):**
+```python
+with open("data.txt", "r") as file:
+ data = file.read()
+# File automatically closed here
+```
+
+
+**Question 3:** What will happen after this code executes?
+```python
+with open("test.txt", "w") as file:
+ file.write("First line\n")
+
+with open("test.txt", "w") as file:
+ file.write("Second line\n")
+```
+
+Show Answer
+
+**The file will only contain:** `"Second line\n"`
+
+**Explanation:** Opening a file in `'w'` mode **truncates** (clears) the file. The first write adds "First line\n", but the second `open()` with `'w'` mode clears everything and writes "Second line\n".
+
+To keep both lines, use `'a'` (append) mode for the second operation.
+
+
+---
## Error Handling in File I/O
Always handle file operations with care to avoid exceptions:
-````python
+```python
try:
with open("config.txt", "r") as file:
config = file.read()
@@ -114,25 +267,81 @@ except FileNotFoundError:
print("File not found.")
except IOError:
print("Error while handling the file.")
-````
+```
## File Paths
You can also handle file paths using the `os` or `pathlib` module:
-````python
+```python
from pathlib import Path
file_path = Path("docs") / "myfile.txt"
with open(file_path, "r") as file:
print(file.read())
-````
+```
+
+---
+
+## 🎯 Quiz 4: Error Handling & File Paths
+**Question 1:** What exception is raised when you try to open a file that doesn't exist in read mode?
+
+Show Answer
+
+**`FileNotFoundError`**
+
+Example:
+```python
+try:
+ with open("nonexistent.txt", "r") as file:
+ content = file.read()
+except FileNotFoundError:
+ print("The file doesn't exist!")
+```
+
+
+**Question 2:** What happens if you try to open a file in `'x'` mode and the file already exists?
+
+Show Answer
+
+A **`FileExistsError`** is raised. The `'x'` mode is used to create a new file and will fail if the file already exists, preventing accidental overwrites.
+
+```python
+try:
+ with open("existing.txt", "x") as file:
+ file.write("New content")
+except FileExistsError:
+ print("File already exists!")
+```
+
+
+**Question 3:** Which module provides a more modern, object-oriented approach to handling file paths?
+
+Show Answer
+
+**`pathlib`** module (specifically the `Path` class)
+
+```python
+from pathlib import Path
+
+# Modern approach
+file_path = Path("folder") / "subfolder" / "file.txt"
+
+# Old approach
+import os
+file_path = os.path.join("folder", "subfolder", "file.txt")
+```
+
+The `pathlib` approach is more readable and cross-platform compatible.
+
+
+---
## Example: Reading & Writing
-````python
+```python
# Write to a file
with open("sample.txt", "w") as file:
file.write("This is a test.")
@@ -140,13 +349,153 @@ with open("sample.txt", "w") as file:
# Read the file
with open("sample.txt", "r") as file:
print(file.read())
-````
+```
-## Summary
+## Summary
* Use `open()` to access files.
* Use `read()`, `readline()`, or `readlines()` to read.
* Use `write()` or `writelines()` to write.
* Always use `with` to handle files safely.
-* Handle exceptions for robustness.
\ No newline at end of file
+* Handle exceptions for robustness.
+
+---
+
+## 🎯 Final Quiz: Comprehensive File I/O Challenge
+
+**Question 1:** Write code to read a file line by line and print only non-empty lines.
+
+Show Answer
+
+```python
+with open("data.txt", "r") as file:
+ for line in file:
+ if line.strip(): # Check if line is not empty
+ print(line.strip())
+```
+
+**Alternative using `readlines()`:**
+```python
+with open("data.txt", "r") as file:
+ lines = file.readlines()
+ for line in lines:
+ if line.strip():
+ print(line.strip())
+```
+
+
+**Question 2:** What's wrong with this code?
+```python
+file = open("log.txt", "a")
+file.write("Error occurred\n")
+file.write("System crashed\n")
+```
+
+Show Answer
+
+**Problems:**
+1. File is never closed (resource leak)
+2. No error handling
+3. Not using `with` statement
+
+**Corrected version:**
+```python
+try:
+ with open("log.txt", "a") as file:
+ file.write("Error occurred\n")
+ file.write("System crashed\n")
+except IOError as e:
+ print(f"Error writing to file: {e}")
+```
+
+
+**Question 3:** How would you copy the contents of one file to another?
+
+Show Answer
+
+```python
+try:
+ with open("source.txt", "r") as source:
+ content = source.read()
+
+ with open("destination.txt", "w") as dest:
+ dest.write(content)
+
+ print("File copied successfully!")
+except FileNotFoundError:
+ print("Source file not found!")
+except IOError as e:
+ print(f"Error during file operation: {e}")
+```
+
+**Alternative (line by line):**
+```python
+with open("source.txt", "r") as source:
+ with open("destination.txt", "w") as dest:
+ for line in source:
+ dest.write(line)
+```
+
+
+**Question 4:** Create a function that counts the number of lines, words, and characters in a file.
+
+Show Answer
+
+```python
+def count_file_stats(filename):
+ """Count lines, words, and characters in a file."""
+ try:
+ with open(filename, "r") as file:
+ lines = file.readlines()
+
+ line_count = len(lines)
+ word_count = sum(len(line.split()) for line in lines)
+ char_count = sum(len(line) for line in lines)
+
+ return {
+ "lines": line_count,
+ "words": word_count,
+ "characters": char_count
+ }
+ except FileNotFoundError:
+ print(f"File '{filename}' not found!")
+ return None
+
+# Usage
+stats = count_file_stats("document.txt")
+if stats:
+ print(f"Lines: {stats['lines']}")
+ print(f"Words: {stats['words']}")
+ print(f"Characters: {stats['characters']}")
+```
+
+
+**Question 5:** What mode combination would you use to read and write to a binary file?
+
+Show Answer
+
+**`'rb+'` or `'wb+'`**
+
+- **`'rb+'`**: Read and write binary (file must exist)
+- **`'wb+'`**: Write and read binary (creates new file or truncates)
+- **`'ab+'`**: Append and read binary
+
+**Example - Working with binary files:**
+```python
+# Write binary data
+with open("data.bin", "wb") as file:
+ file.write(b'\x00\x01\x02\x03')
+
+# Read binary data
+with open("data.bin", "rb") as file:
+ data = file.read()
+ print(data) # Output: b'\x00\x01\x02\x03'
+```
+
+Common use cases: images, audio files, encrypted data, etc.
+
+
+---
+
+🎉 **Congratulations!** You've mastered File I/O in Python. Practice with real files to reinforce these concepts!
\ No newline at end of file
diff --git a/docs/python/python-libraries.md b/docs/python/python-libraries.md
new file mode 100644
index 00000000..5dcdfeea
--- /dev/null
+++ b/docs/python/python-libraries.md
@@ -0,0 +1,767 @@
+---
+id: python-libraries
+title: Python Libraries
+sidebar_label: Python Libraries
+sidebar_position: 15
+tags:
+ [
+ Python,
+ Libraries,
+ Modules,
+ Packages,
+ Import,
+ Python Standard Library
+ ]
+
+---
+
+# Python Libraries
+
+A **library** in Python is a collection of modules and packages that provide reusable code to perform common tasks. Libraries help developers avoid writing code from scratch and enable faster, more efficient development.
+
+Python has a rich ecosystem of libraries ranging from the **Standard Library** (built-in) to **third-party libraries** that can be installed via pip.
+
+
+## 1. What is a Python Library?
+
+A library is a collection of pre-written code that you can use in your programs. It includes:
+
+* **Modules**: Single Python files containing functions, classes, and variables
+* **Packages**: Directories containing multiple modules
+* **Libraries**: Collections of packages and modules
+
+Think of a library as a toolbox filled with tools (functions) that help you build your programs faster.
+
+```python
+# Using the math library
+import math
+
+result = math.sqrt(16)
+print(result) # Output: 4.0
+```
+
+
+## 2. Types of Python Libraries
+
+### 2.1 Standard Library
+
+Built-in libraries that come with Python installation. No need to install separately.
+
+**Examples:**
+* `os` - Operating system operations
+* `sys` - System-specific parameters
+* `math` - Mathematical functions
+* `datetime` - Date and time handling
+* `json` - JSON data handling
+* `random` - Random number generation
+
+### 2.2 External/Third-Party Libraries
+
+Libraries created by the Python community that need to be installed using `pip`.
+
+**Examples:**
+* `numpy` - Numerical computing
+* `pandas` - Data analysis
+* `matplotlib` - Data visualization
+* `requests` - HTTP requests
+* `flask` - Web development
+* `django` - Web framework
+
+
+## 3. Importing Libraries
+
+Python provides several ways to import libraries:
+
+### 3.1 Basic Import
+
+```python
+import math
+
+print(math.pi) # 3.141592653589793
+print(math.sqrt(25)) # 5.0
+```
+
+### 3.2 Import with Alias
+
+```python
+import numpy as np
+
+arr = np.array([1, 2, 3])
+print(arr)
+```
+
+### 3.3 Import Specific Functions
+
+```python
+from math import sqrt, pi
+
+print(sqrt(16)) # 4.0
+print(pi) # 3.141592653589793
+```
+
+### 3.4 Import All (Not Recommended)
+
+```python
+from math import *
+
+# Can use all functions directly
+print(sqrt(9)) # 3.0
+```
+
+⚠️ **Warning:** Using `import *` can lead to namespace conflicts and is generally discouraged.
+
+---
+
+## 📝 Quiz 1: Library Basics
+
+**Q1. What is a Python library?**
+- A) A physical building where Python code is stored
+- B) A collection of pre-written code with modules and packages
+- C) A Python variable type
+- D) A Python IDE
+
+
+Show Answer
+**Answer: B) A collection of pre-written code with modules and packages**
+
+A library is a reusable collection of modules and packages that provide functionality without writing code from scratch.
+
+
+**Q2. Which of the following is a Standard Library in Python?**
+- A) numpy
+- B) pandas
+- C) math
+- D) flask
+
+
+Show Answer
+**Answer: C) math**
+
+The `math` library is part of Python's Standard Library and comes built-in. NumPy, Pandas, and Flask are third-party libraries.
+
+
+---
+
+## 4. Popular Standard Libraries
+
+### 4.1 `math` - Mathematical Operations
+
+```python
+import math
+
+# Common functions
+print(math.sqrt(64)) # 8.0
+print(math.pow(2, 3)) # 8.0
+print(math.ceil(4.3)) # 5
+print(math.floor(4.7)) # 4
+print(math.factorial(5)) # 120
+
+# Constants
+print(math.pi) # 3.141592653589793
+print(math.e) # 2.718281828459045
+```
+
+### 4.2 `random` - Random Number Generation
+
+```python
+import random
+
+# Random integer
+print(random.randint(1, 10)) # Random number between 1 and 10
+
+# Random choice from list
+colors = ['red', 'green', 'blue']
+print(random.choice(colors))
+
+# Shuffle list
+numbers = [1, 2, 3, 4, 5]
+random.shuffle(numbers)
+print(numbers)
+
+# Random float between 0 and 1
+print(random.random())
+```
+
+### 4.3 `datetime` - Date and Time
+
+```python
+from datetime import datetime, date, timedelta
+
+# Current date and time
+now = datetime.now()
+print(now)
+
+# Current date
+today = date.today()
+print(today) # 2025-10-04
+
+# Date arithmetic
+tomorrow = today + timedelta(days=1)
+print(tomorrow)
+
+# Formatting dates
+formatted = now.strftime("%Y-%m-%d %H:%M:%S")
+print(formatted)
+```
+
+### 4.4 `os` - Operating System Interface
+
+```python
+import os
+
+# Get current directory
+print(os.getcwd())
+
+# List files in directory
+print(os.listdir('.'))
+
+# Create directory
+# os.mkdir('new_folder')
+
+# Check if path exists
+print(os.path.exists('file.txt'))
+
+# Get environment variables
+print(os.environ.get('HOME'))
+```
+
+---
+
+## 📝 Quiz 2: Standard Libraries
+
+**Q1. Which library is used for generating random numbers?**
+- A) math
+- B) random
+- C) datetime
+- D) os
+
+
+Show Answer
+**Answer: B) random**
+
+The `random` library provides functions for generating random numbers, making random choices, and shuffling sequences.
+
+
+**Q2. What does `math.ceil(4.3)` return?**
+- A) 4
+- B) 4.0
+- C) 5
+- D) 4.3
+
+
+Show Answer
+**Answer: C) 5**
+
+The `ceil()` function rounds a number UP to the nearest integer, so 4.3 becomes 5.
+
+
+---
+
+## 5. Installing Third-Party Libraries
+
+Use **pip** (Python Package Installer) to install external libraries.
+
+### Installation Commands:
+
+```bash
+# Install a library
+pip install numpy
+
+# Install specific version
+pip install numpy==1.21.0
+
+# Install multiple libraries
+pip install numpy pandas matplotlib
+
+# Upgrade a library
+pip install --upgrade numpy
+
+# Uninstall a library
+pip uninstall numpy
+
+# List installed libraries
+pip list
+
+# Show library information
+pip show numpy
+```
+
+### Using requirements.txt
+
+Create a `requirements.txt` file to manage project dependencies:
+
+```text
+numpy==1.21.0
+pandas==1.3.0
+matplotlib==3.4.2
+requests==2.26.0
+```
+
+Install all dependencies:
+
+```bash
+pip install -r requirements.txt
+```
+
+---
+
+## 📝 Quiz 3: Installing Libraries
+
+**Q1. Which command installs a Python library?**
+- A) `python install numpy`
+- B) `pip install numpy`
+- C) `install numpy`
+- D) `npm install numpy`
+
+
+Show Answer
+**Answer: B) `pip install numpy`**
+
+`pip install` is the correct command to install Python packages from PyPI (Python Package Index).
+
+
+**Q2. What is the purpose of requirements.txt?**
+- A) To write code requirements
+- B) To list project dependencies for easy installation
+- C) To document user requirements
+- D) To store configuration settings
+
+
+Show Answer
+**Answer: B) To list project dependencies for easy installation**
+
+`requirements.txt` contains a list of all packages needed for a project, making it easy to install them all at once.
+
+
+---
+
+## 6. Popular Third-Party Libraries
+
+### 6.1 NumPy - Numerical Computing
+
+```python
+import numpy as np
+
+# Create arrays
+arr = np.array([1, 2, 3, 4, 5])
+print(arr)
+
+# Array operations
+print(arr * 2) # [2, 4, 6, 8, 10]
+print(arr + 10) # [11, 12, 13, 14, 15]
+
+# Multi-dimensional arrays
+matrix = np.array([[1, 2, 3], [4, 5, 6]])
+print(matrix)
+
+# Statistical functions
+print(np.mean(arr)) # 3.0
+print(np.median(arr)) # 3.0
+print(np.std(arr)) # Standard deviation
+```
+
+### 6.2 Pandas - Data Analysis
+
+```python
+import pandas as pd
+
+# Create DataFrame
+data = {
+ 'Name': ['Alice', 'Bob', 'Charlie'],
+ 'Age': [25, 30, 35],
+ 'City': ['New York', 'London', 'Paris']
+}
+df = pd.DataFrame(data)
+print(df)
+
+# Read CSV file
+# df = pd.read_csv('data.csv')
+
+# Basic operations
+print(df['Age'].mean()) # Average age
+print(df.describe()) # Statistical summary
+print(df.head()) # First 5 rows
+```
+
+### 6.3 Requests - HTTP Library
+
+```python
+import requests
+
+# GET request
+response = requests.get('https://api.github.com')
+print(response.status_code) # 200
+print(response.json()) # JSON response
+
+# POST request
+data = {'key': 'value'}
+response = requests.post('https://httpbin.org/post', json=data)
+print(response.json())
+```
+
+### 6.4 Matplotlib - Data Visualization
+
+```python
+import matplotlib.pyplot as plt
+
+# Simple line plot
+x = [1, 2, 3, 4, 5]
+y = [2, 4, 6, 8, 10]
+
+plt.plot(x, y)
+plt.xlabel('X-axis')
+plt.ylabel('Y-axis')
+plt.title('Simple Line Plot')
+plt.show()
+
+# Bar chart
+categories = ['A', 'B', 'C', 'D']
+values = [10, 25, 15, 30]
+
+plt.bar(categories, values)
+plt.title('Bar Chart Example')
+plt.show()
+```
+
+---
+
+## 📝 Quiz 4: Third-Party Libraries
+
+**Q1. Which library is primarily used for data analysis in Python?**
+- A) NumPy
+- B) Pandas
+- C) Matplotlib
+- D) Requests
+
+
+Show Answer
+**Answer: B) Pandas**
+
+Pandas is specifically designed for data manipulation and analysis, working with DataFrames and Series.
+
+
+**Q2. What does NumPy primarily work with?**
+- A) Web requests
+- B) Arrays and numerical operations
+- C) Database connections
+- D) File operations
+
+
+Show Answer
+**Answer: B) Arrays and numerical operations**
+
+NumPy is designed for numerical computing and provides powerful array operations and mathematical functions.
+
+
+---
+
+## 7. Creating Your Own Library
+
+You can create your own library by organizing code into modules.
+
+### Step 1: Create a module file `mylib.py`
+
+```python
+# mylib.py
+
+def greet(name):
+ return f"Hello, {name}!"
+
+def add(a, b):
+ return a + b
+
+def multiply(a, b):
+ return a * b
+
+PI = 3.14159
+```
+
+### Step 2: Import and use your module
+
+```python
+# main.py
+import mylib
+
+print(mylib.greet("Alice")) # Hello, Alice!
+print(mylib.add(5, 3)) # 8
+print(mylib.PI) # 3.14159
+```
+
+### Creating a Package
+
+Create a directory structure:
+
+```
+mypackage/
+ __init__.py
+ math_ops.py
+ string_ops.py
+```
+
+**math_ops.py:**
+```python
+def add(a, b):
+ return a + b
+
+def subtract(a, b):
+ return a - b
+```
+
+**string_ops.py:**
+```python
+def capitalize(text):
+ return text.upper()
+
+def reverse(text):
+ return text[::-1]
+```
+
+**Usage:**
+```python
+from mypackage import math_ops, string_ops
+
+print(math_ops.add(5, 3))
+print(string_ops.capitalize("hello"))
+```
+
+---
+
+## 📝 Quiz 5: Creating Libraries
+
+**Q1. What file is required to make a directory a Python package?**
+- A) `main.py`
+- B) `__init__.py`
+- C) `package.py`
+- D) `setup.py`
+
+
+Show Answer
+**Answer: B) `__init__.py`**
+
+The `__init__.py` file (even if empty) tells Python that the directory should be treated as a package.
+
+
+**Q2. How do you import a specific function from your custom module?**
+- A) `import function from mylib`
+- B) `from mylib import function`
+- C) `mylib.import(function)`
+- D) `get function from mylib`
+
+
+Show Answer
+**Answer: B) `from mylib import function`**
+
+The correct syntax is `from module_name import function_name` to import specific functions.
+
+
+---
+
+## 8. Library Best Practices
+
+### ✅ Do's:
+
+* Use meaningful import aliases (`import numpy as np`)
+* Import only what you need
+* Keep requirements.txt updated
+* Use virtual environments for project isolation
+* Read library documentation
+* Check library compatibility and maintenance
+
+### ❌ Don'ts:
+
+* Avoid `from module import *`
+* Don't install libraries globally for all projects
+* Don't use outdated or unmaintained libraries
+* Don't ignore version compatibility
+* Avoid circular imports
+
+### Virtual Environments
+
+Create isolated environments for different projects:
+
+```bash
+# Create virtual environment
+python -m venv myenv
+
+# Activate (Windows)
+myenv\Scripts\activate
+
+# Activate (Mac/Linux)
+source myenv/bin/activate
+
+# Deactivate
+deactivate
+```
+
+---
+
+## 9. Finding and Choosing Libraries
+
+### Where to Find Libraries:
+
+* **PyPI (Python Package Index)**: https://pypi.org
+* **GitHub**: Search for Python projects
+* **Awesome Python**: Curated list of libraries
+* **Python Documentation**: Official library reference
+
+### How to Choose:
+
+1. **Check popularity**: GitHub stars, downloads
+2. **Read documentation**: Well-documented libraries are easier to use
+3. **Check maintenance**: Recent updates and active issues
+4. **License compatibility**: Ensure license matches your needs
+5. **Community support**: Active community means better support
+
+---
+
+## 📝 Final Quiz: Comprehensive Review
+
+**Q1. What is the difference between a module and a package?**
+- A) There is no difference
+- B) A module is a single file; a package is a directory of modules
+- C) A package is smaller than a module
+- D) Modules are built-in; packages are external
+
+
+Show Answer
+**Answer: B) A module is a single file; a package is a directory of modules**
+
+A module is a single Python file containing code, while a package is a directory containing multiple modules and an `__init__.py` file.
+
+
+**Q2. Which command shows information about an installed library?**
+- A) `pip info numpy`
+- B) `pip show numpy`
+- C) `pip describe numpy`
+- D) `pip details numpy`
+
+
+Show Answer
+**Answer: B) `pip show numpy`**
+
+The `pip show` command displays detailed information about an installed package.
+
+
+**Q3. Why should you use virtual environments?**
+- A) To make Python run faster
+- B) To isolate project dependencies and avoid conflicts
+- C) To create virtual machines
+- D) To backup your code
+
+
+Show Answer
+**Answer: B) To isolate project dependencies and avoid conflicts**
+
+Virtual environments create isolated Python environments for each project, preventing dependency conflicts between projects.
+
+
+**Q4. What does `import numpy as np` do?**
+- A) Installs NumPy
+- B) Imports NumPy and creates an alias 'np'
+- C) Imports only the 'np' module from NumPy
+- D) Creates a copy of NumPy
+
+
+Show Answer
+**Answer: B) Imports NumPy and creates an alias 'np'**
+
+The `as` keyword creates an alias, allowing you to use `np` instead of typing `numpy` every time.
+
+
+**Q5. Which library would you use to make HTTP requests?**
+- A) http
+- B) urllib
+- C) requests
+- D) web
+
+
+Show Answer
+**Answer: C) requests**
+
+The `requests` library is the most popular and user-friendly library for making HTTP requests in Python.
+
+
+---
+
+## 10. Example Project: Weather App
+
+Here's a practical example using multiple libraries:
+
+```python
+import requests
+from datetime import datetime
+import json
+
+def get_weather(city):
+ """
+ Fetch weather data for a city using OpenWeather API
+ """
+ api_key = "your_api_key_here"
+ base_url = "http://api.openweathermap.org/data/2.5/weather"
+
+ params = {
+ 'q': city,
+ 'appid': api_key,
+ 'units': 'metric'
+ }
+
+ try:
+ response = requests.get(base_url, params=params)
+ response.raise_for_status()
+ data = response.json()
+
+ # Extract information
+ temp = data['main']['temp']
+ description = data['weather'][0]['description']
+ humidity = data['main']['humidity']
+
+ # Current time
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+ print(f"\n=== Weather in {city} ===")
+ print(f"Time: {current_time}")
+ print(f"Temperature: {temp}°C")
+ print(f"Description: {description.capitalize()}")
+ print(f"Humidity: {humidity}%")
+
+ except requests.exceptions.RequestException as e:
+ print(f"Error fetching weather data: {e}")
+
+# Usage
+get_weather("London")
+get_weather("New York")
+```
+
+**Output:**
+```
+=== Weather in London ===
+Time: 2025-10-04 14:30:00
+Temperature: 18.5°C
+Description: Partly cloudy
+Humidity: 65%
+```
+
+---
+
+## Summary
+
+* **Libraries** are collections of pre-written code that provide reusable functionality
+* **Standard Library** comes built-in with Python (math, random, datetime, os)
+* **Third-party libraries** need to be installed using `pip` (numpy, pandas, requests)
+* Use **import** statements to include libraries in your code
+* Create your own libraries by organizing code into modules and packages
+* Use **virtual environments** to manage project dependencies
+* Choose libraries based on popularity, documentation, and maintenance
+* **Best practices**: Import only what you need, use aliases, keep dependencies updated
+
+### Key Takeaways
+
+* Libraries save development time and provide tested, reliable code
+* Python's ecosystem has libraries for almost every task
+* Understanding how to find, install, and use libraries is essential for Python development
+* Creating your own libraries helps organize and reuse code across projects
+* Always use virtual environments for project isolation
+
+---
+
+> 📌 **Pro Tip**: Before installing a new library, always check its documentation, GitHub repository, and recent activity to ensure it's well-maintained and suitable for your needs!
\ No newline at end of file
diff --git a/docs/python/python-list.md b/docs/python/python-list.md
index 55b8ece6..e1091e9a 100644
--- a/docs/python/python-list.md
+++ b/docs/python/python-list.md
@@ -39,7 +39,7 @@ fruits = ["apple", "banana", "cherry"]
# Mixed Data Types
mixed = [1, "hello", 3.14, True]
-````
+```
@@ -65,7 +65,51 @@ print(fruits[-2]) # banana
print(fruits[-3]) # apple
```
+---
+
+## 🎯 Quiz 1: List Basics & Indexing
+
+**Question 1:** What are the three key characteristics of Python lists?
+
+Show Answer
+
+1. **Ordered** - Items maintain their position
+2. **Mutable** - You can change, add, or remove items
+3. **Can contain different data types** - Mix integers, strings, booleans, etc.
+
+```python
+my_list = [1, "hello", True, 3.14] # All valid in one list!
+```
+
+
+**Question 2:** What will be the output?
+```python
+numbers = [10, 20, 30, 40, 50]
+print(numbers[-2])
+```
+
+Show Answer
+
+**Output:** `40`
+
+**Explanation:** Negative indexing starts from the end:
+- `numbers[-1]` = 50 (last item)
+- `numbers[-2]` = 40 (second-to-last item)
+- `numbers[-3]` = 30
+
+
+**Question 3:** What error occurs when you try to access `fruits[5]` if `fruits = ["apple", "banana", "cherry"]`?
+
+Show Answer
+
+**`IndexError: list index out of range`**
+
+The list only has 3 elements (indices 0, 1, 2), so trying to access index 5 raises an error.
+**Valid indices:** 0, 1, 2, -1, -2, -3
+
+
+---
## Slicing
@@ -103,6 +147,65 @@ fruits[1] = "mango"
print(fruits) # ['apple', 'mango', 'cherry']
```
+---
+
+## 🎯 Quiz 2: Slicing & Mutability
+
+**Question 1:** What will be the output?
+```python
+letters = ['a', 'b', 'c', 'd', 'e', 'f']
+print(letters[1:4])
+```
+
+Show Answer
+
+**Output:** `['b', 'c', 'd']`
+
+**Explanation:** Slicing `[1:4]` means:
+- Start at index 1 ('b')
+- Stop **before** index 4
+- So it includes indices 1, 2, 3
+
+
+**Question 2:** How would you reverse a list using slicing?
+
+Show Answer
+
+**Use `[::-1]`**
+
+```python
+numbers = [1, 2, 3, 4, 5]
+reversed_nums = numbers[::-1]
+print(reversed_nums) # [5, 4, 3, 2, 1]
+```
+
+The `-1` step means "go backwards through the entire list."
+
+
+**Question 3:** What's the difference between these two?
+```python
+# Option A
+nums = [1, 2, 3]
+nums[1] = 99
+
+# Option B
+nums = (1, 2, 3)
+nums[1] = 99
+```
+
+Show Answer
+
+**Option A:** Works fine! Lists are **mutable**, so you can change elements.
+```python
+# Result: [1, 99, 3]
+```
+
+**Option B:** Raises `TypeError: 'tuple' object does not support item assignment`
+
+Tuples are **immutable** - you cannot change their elements after creation.
+
+
+---
## List Methods
@@ -161,6 +264,63 @@ nums.pop(2) # Removes index 2
print(nums) # [1, 2, 4, 5]
```
+---
+
+## 🎯 Quiz 3: List Methods
+
+**Question 1:** What's the difference between `append()` and `extend()`?
+
+Show Answer
+
+**`append()`** adds a **single item** (even if it's a list):
+```python
+nums = [1, 2, 3]
+nums.append([4, 5])
+print(nums) # [1, 2, 3, [4, 5]]
+```
+
+**`extend()`** adds **all items** from an iterable:
+```python
+nums = [1, 2, 3]
+nums.extend([4, 5])
+print(nums) # [1, 2, 3, 4, 5]
+```
+
+
+**Question 2:** What will happen?
+```python
+fruits = ["apple", "banana", "apple", "cherry", "apple"]
+fruits.remove("apple")
+print(fruits)
+```
+
+Show Answer
+
+**Output:** `['banana', 'apple', 'cherry', 'apple']`
+
+**Explanation:** `remove()` only removes the **first occurrence** of the item. The other two "apple" entries remain in the list.
+
+
+**Question 3:** What does `pop()` return?
+```python
+numbers = [10, 20, 30, 40]
+x = numbers.pop(1)
+print(x)
+print(numbers)
+```
+
+Show Answer
+
+**Output:**
+```
+20
+[10, 30, 40]
+```
+
+**Explanation:** `pop(i)` **removes** the item at index `i` and **returns** it. So `x` gets the value 20, and the list no longer contains it.
+
+
+---
## Iterating Through a List
@@ -214,6 +374,77 @@ print(matrix[0]) # [1, 2, 3]
print(matrix[1][2]) # 6
```
+---
+
+## 🎯 Quiz 4: Iteration & Nested Lists
+
+**Question 1:** How would you print both the index and value of each item in a list?
+
+Show Answer
+
+**Use `enumerate()`:**
+```python
+fruits = ["apple", "banana", "cherry"]
+
+for index, fruit in enumerate(fruits):
+ print(f"{index}: {fruit}")
+```
+
+**Output:**
+```
+0: apple
+1: banana
+2: cherry
+```
+
+This is more Pythonic than using `range(len(fruits))`!
+
+
+**Question 2:** What will be the output?
+```python
+matrix = [
+ [1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9]
+]
+print(matrix[2][0])
+```
+
+Show Answer
+
+**Output:** `7`
+
+**Explanation:**
+- `matrix[2]` gets the third sublist: `[7, 8, 9]`
+- `matrix[2][0]` gets the first element of that sublist: `7`
+
+Think of it as: row 2, column 0
+
+
+**Question 3:** How do you check if a list is empty?
+
+Show Answer
+
+**Two common ways:**
+
+**Method 1: Using `len()`**
+```python
+my_list = []
+if len(my_list) == 0:
+ print("List is empty")
+```
+
+**Method 2: Direct boolean check (Pythonic)**
+```python
+my_list = []
+if not my_list:
+ print("List is empty")
+```
+
+Empty lists evaluate to `False` in a boolean context, so Method 2 is preferred!
+
+
+---
## List Comprehensions
@@ -254,7 +485,207 @@ print(a) # [1, 2, 3, 4]
print(c) # [1, 2, 3, 4, 5]
```
+---
+
+## 🎯 Quiz 5: List Comprehensions & Copying
+
+**Question 1:** What will this list comprehension produce?
+```python
+result = [x * 2 for x in range(5) if x > 2]
+print(result)
+```
+
+Show Answer
+
+**Output:** `[6, 8]`
+
+**Breakdown:**
+- `range(5)` gives: 0, 1, 2, 3, 4
+- Filter `if x > 2`: only 3 and 4 qualify
+- Multiply by 2: `[3*2, 4*2]` = `[6, 8]`
+
+
+**Question 2:** What's wrong with this code?
+```python
+original = [1, 2, 3]
+copy = original
+copy.append(4)
+print(original)
+```
+
+Show Answer
+
+**Problem:** This doesn't create a copy! Both variables point to the **same list**.
+
+**Output:** `[1, 2, 3, 4]` (original is modified!)
+
+**Fix - Create a real copy:**
+```python
+original = [1, 2, 3]
+copy = original.copy() # or original[:]
+copy.append(4)
+print(original) # [1, 2, 3] (unchanged)
+print(copy) # [1, 2, 3, 4]
+```
+
+
+**Question 3:** Rewrite this loop as a list comprehension:
+```python
+result = []
+for i in range(1, 11):
+ if i % 3 == 0:
+ result.append(i)
+```
+
+Show Answer
+
+**List comprehension version:**
+```python
+result = [i for i in range(1, 11) if i % 3 == 0]
+print(result) # [3, 6, 9]
+```
+
+Much more concise and Pythonic!
+
+
+---
## Conclusion
Python Lists are a **powerful and flexible** data structure used everywhere—from collecting and processing data to building complex programs. Practice using list methods and experiment to become confident.
+
+---
+
+## 🎯 Final Quiz: Comprehensive List Challenge
+
+**Question 1:** What will be the final output?
+```python
+nums = [1, 2, 3, 4, 5]
+nums.append(6)
+nums.insert(0, 0)
+nums.pop()
+nums.reverse()
+print(nums)
+```
+
+Show Answer
+
+**Output:** `[5, 4, 3, 2, 1, 0]`
+
+**Step-by-step:**
+1. Start: `[1, 2, 3, 4, 5]`
+2. `append(6)`: `[1, 2, 3, 4, 5, 6]`
+3. `insert(0, 0)`: `[0, 1, 2, 3, 4, 5, 6]`
+4. `pop()`: `[0, 1, 2, 3, 4, 5]` (removes 6)
+5. `reverse()`: `[5, 4, 3, 2, 1, 0]`
+
+
+**Question 2:** Remove all duplicates from a list while preserving order.
+```python
+numbers = [1, 2, 2, 3, 4, 3, 5, 1]
+# Your solution here
+```
+
+Show Answer
+
+**Solution 1: Using a loop**
+```python
+numbers = [1, 2, 2, 3, 4, 3, 5, 1]
+unique = []
+for num in numbers:
+ if num not in unique:
+ unique.append(num)
+print(unique) # [1, 2, 3, 4, 5]
+```
+
+**Solution 2: Using dict (Python 3.7+)**
+```python
+numbers = [1, 2, 2, 3, 4, 3, 5, 1]
+unique = list(dict.fromkeys(numbers))
+print(unique) # [1, 2, 3, 4, 5]
+```
+
+Note: `set(numbers)` removes duplicates but doesn't preserve order!
+
+
+**Question 3:** Create a function that finds the second largest number in a list.
+
+Show Answer
+
+```python
+def second_largest(numbers):
+ """Find the second largest number in a list."""
+ if len(numbers) < 2:
+ return None
+
+ # Remove duplicates and sort
+ unique_nums = list(set(numbers))
+ unique_nums.sort(reverse=True)
+
+ return unique_nums[1] if len(unique_nums) > 1 else None
+
+# Test
+nums = [10, 5, 8, 12, 3, 12, 7]
+print(second_largest(nums)) # 10
+
+# Alternative one-liner
+def second_largest(numbers):
+ return sorted(set(numbers))[-2] if len(set(numbers)) > 1 else None
+```
+
+
+**Question 4:** What's the output of this nested list comprehension?
+```python
+matrix = [[1, 2], [3, 4], [5, 6]]
+flattened = [num for row in matrix for num in row]
+print(flattened)
+```
+
+Show Answer
+
+**Output:** `[1, 2, 3, 4, 5, 6]`
+
+**Explanation:** This flattens the 2D list into a 1D list.
+
+**Equivalent loop:**
+```python
+flattened = []
+for row in matrix:
+ for num in row:
+ flattened.append(num)
+```
+
+Read nested comprehensions **left to right** like nested loops!
+
+
+**Question 5:** Write a function to rotate a list by `n` positions.
+```python
+# rotate_list([1, 2, 3, 4, 5], 2) → [4, 5, 1, 2, 3]
+```
+
+Show Answer
+
+```python
+def rotate_list(lst, n):
+ """Rotate list to the right by n positions."""
+ if not lst:
+ return lst
+
+ n = n % len(lst) # Handle n larger than list length
+ return lst[-n:] + lst[:-n]
+
+# Tests
+print(rotate_list([1, 2, 3, 4, 5], 2)) # [4, 5, 1, 2, 3]
+print(rotate_list([1, 2, 3, 4, 5], 7)) # [4, 5, 1, 2, 3] (7 % 5 = 2)
+print(rotate_list([1, 2, 3], 0)) # [1, 2, 3]
+
+# How it works:
+# lst[-n:] gets last n elements: [4, 5]
+# lst[:-n] gets everything except last n: [1, 2, 3]
+# Concatenate: [4, 5, 1, 2, 3]
+```
+
+
+---
+
+🎉 **Congratulations!** You've mastered Python Lists. Keep practicing with real-world problems to strengthen your skills!
\ No newline at end of file
diff --git a/docs/python/python-loops.md b/docs/python/python-loops.md
index 4dfb7be9..3344f51a 100644
--- a/docs/python/python-loops.md
+++ b/docs/python/python-loops.md
@@ -70,6 +70,65 @@ for i in range(0, 10, 2):
4
```
+---
+
+## 🎯 Quiz 1: For Loops & Range Function
+
+**Question 1:** What will be the output?
+```python
+for i in range(3):
+ print(i)
+```
+
+Show Answer
+
+**Output:**
+```
+0
+1
+2
+```
+
+**Explanation:** `range(3)` generates numbers from 0 up to (but not including) 3. So it produces: 0, 1, 2.
+
+
+**Question 2:** How would you use `range()` to print only odd numbers from 1 to 9?
+
+Show Answer
+
+```python
+for i in range(1, 10, 2):
+ print(i)
+```
+
+**Output:**
+```
+1
+3
+5
+7
+9
+```
+
+**Explanation:** `range(start, stop, step)` where:
+- `start = 1` (first odd number)
+- `stop = 10` (goes up to but not including 10)
+- `step = 2` (skip every other number)
+
+
+**Question 3:** What's the difference between `range(5)` and `range(1, 6)`?
+
+Show Answer
+
+**`range(5)`:** Produces `0, 1, 2, 3, 4` (starts from 0)
+
+**`range(1, 6)`:** Produces `1, 2, 3, 4, 5` (starts from 1)
+
+Both generate 5 numbers, but with different starting points. Remember: the stop value is always **exclusive** (not included).
+
+
+---
+
## The `while` Loop
The `while` loop continues executing as long as a condition is **True**.
@@ -99,6 +158,88 @@ Count: 4
Count: 5
```
+---
+
+## 🎯 Quiz 2: While Loops
+
+**Question 1:** What's the most common mistake that leads to an infinite loop?
+
+Show Answer
+
+**Forgetting to update the condition variable!**
+
+**Infinite loop example:**
+```python
+count = 0
+while count < 5:
+ print(count)
+ # Forgot to increment count!
+```
+
+**Fixed version:**
+```python
+count = 0
+while count < 5:
+ print(count)
+ count += 1 # Update the variable!
+```
+
+Always ensure your loop condition will eventually become `False`.
+
+
+**Question 2:** What will happen with this code?
+```python
+x = 10
+while x > 5:
+ print(x)
+ x -= 2
+```
+
+Show Answer
+
+**Output:**
+```
+10
+8
+6
+```
+
+**Explanation:**
+- First iteration: x = 10 (10 > 5 is True, print 10, x becomes 8)
+- Second iteration: x = 8 (8 > 5 is True, print 8, x becomes 6)
+- Third iteration: x = 6 (6 > 5 is True, print 6, x becomes 4)
+- Fourth iteration: x = 4 (4 > 5 is False, loop stops)
+
+
+**Question 3:** When should you use a `while` loop instead of a `for` loop?
+
+Show Answer
+
+Use a **`while` loop** when:
+- You don't know how many iterations you need in advance
+- The loop depends on a condition that may change unpredictably
+- You're waiting for user input or an event
+
+**Examples:**
+```python
+# Unknown iterations - input validation
+password = ""
+while password != "secret":
+ password = input("Enter password: ")
+
+# Unknown iterations - game loop
+game_running = True
+while game_running:
+ # Game logic
+ if player_quits:
+ game_running = False
+```
+
+Use a **`for` loop** when you know the sequence or number of iterations in advance.
+
+
+---
+
## Loop Control Statements
### The `break` Statement
@@ -152,6 +293,94 @@ for i in range(3):
print(i)
```
+---
+
+## 🎯 Quiz 3: Loop Control Statements
+
+**Question 1:** What's the difference between `break` and `continue`?
+
+Show Answer
+
+**`break`:** **Exits** the entire loop immediately
+```python
+for i in range(5):
+ if i == 2:
+ break
+ print(i)
+# Output: 0, 1 (loop stops completely)
+```
+
+**`continue`:** **Skips** the rest of the current iteration and moves to the next one
+```python
+for i in range(5):
+ if i == 2:
+ continue
+ print(i)
+# Output: 0, 1, 3, 4 (skips only i=2)
+```
+
+
+**Question 2:** What will be the output?
+```python
+for i in range(1, 6):
+ if i == 3:
+ continue
+ if i == 5:
+ break
+ print(i)
+```
+
+Show Answer
+
+**Output:**
+```
+1
+2
+4
+```
+
+**Explanation:**
+- i=1: prints 1
+- i=2: prints 2
+- i=3: `continue` skips printing
+- i=4: prints 4
+- i=5: `break` exits loop (5 is never printed)
+
+
+**Question 3:** When would you use the `pass` statement?
+
+Show Answer
+
+Use `pass` when you need a **syntactically valid placeholder** but don't want any action:
+
+**1. During development:**
+```python
+def future_function():
+ pass # Will implement later
+```
+
+**2. In conditional statements:**
+```python
+for num in numbers:
+ if num < 0:
+ pass # Ignore negative numbers for now
+ else:
+ process(num)
+```
+
+**3. In exception handling:**
+```python
+try:
+ risky_operation()
+except ValueError:
+ pass # Silently ignore ValueError
+```
+
+Without `pass`, you'd get a syntax error for an empty block!
+
+
+---
+
## Nested Loops
You can place one loop inside another loop.
@@ -179,6 +408,84 @@ for i in range(1, 4):
3 x 3 = 9
```
+---
+
+## 🎯 Quiz 4: Nested Loops
+
+**Question 1:** How many times will "Hello" be printed?
+```python
+for i in range(3):
+ for j in range(4):
+ print("Hello")
+```
+
+Show Answer
+
+**Answer: 12 times**
+
+**Explanation:**
+- Outer loop runs 3 times
+- For each outer iteration, inner loop runs 4 times
+- Total: 3 × 4 = 12
+
+The pattern: outer iterations × inner iterations = total iterations
+
+
+**Question 2:** What will this code print?
+```python
+for i in range(3):
+ for j in range(i):
+ print("*", end="")
+ print()
+```
+
+Show Answer
+
+**Output:**
+```
+
+*
+**
+```
+
+**Explanation:**
+- i=0: inner loop runs 0 times → (empty line)
+- i=1: inner loop runs 1 time → `*`
+- i=2: inner loop runs 2 times → `**`
+
+This creates a growing triangle pattern!
+
+
+**Question 3:** Can you use `break` in a nested loop? What happens?
+
+Show Answer
+
+**Yes, but `break` only exits the innermost loop it's in.**
+
+```python
+for i in range(3):
+ print(f"Outer loop: {i}")
+ for j in range(3):
+ if j == 1:
+ break # Only breaks inner loop
+ print(f" Inner loop: {j}")
+```
+
+**Output:**
+```
+Outer loop: 0
+ Inner loop: 0
+Outer loop: 1
+ Inner loop: 0
+Outer loop: 2
+ Inner loop: 0
+```
+
+To break out of **all** loops, you need a flag variable or use a function with `return`.
+
+
+---
+
## Looping Through Different Data Types
### Strings
@@ -224,6 +531,77 @@ for index, color in enumerate(colors):
2: blue
```
+---
+
+## 🎯 Quiz 5: Looping Through Data Types
+
+**Question 1:** What will be printed?
+```python
+word = "Hi"
+for char in word:
+ print(char * 2)
+```
+
+Show Answer
+
+**Output:**
+```
+HH
+ii
+```
+
+**Explanation:** The loop iterates through each character ('H', then 'i'), and `char * 2` repeats each character twice.
+
+
+**Question 2:** What's the output?
+```python
+scores = {"math": 90, "english": 85}
+for subject, score in scores.items():
+ print(f"{subject}: {score}")
+```
+
+Show Answer
+
+**Output:**
+```
+math: 90
+english: 85
+```
+
+**Explanation:** `.items()` returns key-value pairs as tuples, which are unpacked into `subject` and `score` variables.
+
+**Note:** Using just `for subject in scores:` would only iterate through the keys!
+
+
+**Question 3:** How would you loop through a list backwards?
+
+Show Answer
+
+**Method 1: Using `reversed()`**
+```python
+fruits = ["apple", "banana", "cherry"]
+for fruit in reversed(fruits):
+ print(fruit)
+# Output: cherry, banana, apple
+```
+
+**Method 2: Using negative step in range with indexing**
+```python
+for i in range(len(fruits) - 1, -1, -1):
+ print(fruits[i])
+```
+
+**Method 3: Using slice notation**
+```python
+for fruit in fruits[::-1]:
+ print(fruit)
+```
+
+**Method 1 is the most Pythonic and readable!**
+
+
+---
+
## The `else` Clause in Loops
Loops can have an `else` clause that executes when the loop completes normally (not broken by `break`).
@@ -280,6 +658,111 @@ for num in numbers:
break
```
+---
+
+## 🎯 Quiz 6: Loop Patterns & Else Clause
+
+**Question 1:** When does the `else` clause of a loop execute?
+
+Show Answer
+
+The `else` clause executes when the loop **completes normally** (without encountering a `break`).
+
+**Example 1: else executes**
+```python
+for i in range(3):
+ print(i)
+else:
+ print("Done!")
+# Output: 0, 1, 2, Done!
+```
+
+**Example 2: else does NOT execute**
+```python
+for i in range(5):
+ if i == 2:
+ break
+ print(i)
+else:
+ print("Done!")
+# Output: 0, 1 (no "Done!" because of break)
+```
+
+**Common use case:** Searching for an item
+```python
+numbers = [1, 3, 5, 7]
+target = 4
+
+for num in numbers:
+ if num == target:
+ print("Found!")
+ break
+else:
+ print("Not found!") # Executes if loop completes without finding
+```
+
+
+**Question 2:** Implement a loop pattern to find the maximum number in a list without using `max()`.
+
+Show Answer
+
+```python
+numbers = [3, 7, 2, 9, 1, 5]
+
+# Initialize with first element
+max_num = numbers[0]
+
+# Compare with rest
+for num in numbers[1:]:
+ if num > max_num:
+ max_num = num
+
+print(f"Maximum: {max_num}") # Output: Maximum: 9
+```
+
+**Alternative: Check for empty list**
+```python
+def find_max(numbers):
+ if not numbers:
+ return None
+
+ max_num = numbers[0]
+ for num in numbers:
+ if num > max_num:
+ max_num = num
+ return max_num
+```
+
+
+**Question 3:** What's wrong with this accumulation pattern?
+```python
+numbers = [1, 2, 3, 4, 5]
+for num in numbers:
+ total = 0
+ total += num
+print(total)
+```
+
+Show Answer
+
+**Problem:** `total = 0` is **inside** the loop, so it resets to 0 every iteration!
+
+**Output:** `5` (only the last number)
+
+**Fixed version:**
+```python
+numbers = [1, 2, 3, 4, 5]
+total = 0 # Initialize OUTSIDE the loop
+for num in numbers:
+ total += num
+print(total) # Output: 15
+```
+
+This is a very common beginner mistake! Always initialize accumulator variables **before** the loop.
+
+
+---
+
## Best Practices
1. **Use `for` loops** when you know the number of iterations
@@ -302,4 +785,168 @@ for num in numbers:
## Conclusion
-Loops are fundamental to programming in Python. Whether you're processing data, automating tasks, or building complex algorithms, mastering `for` and `while` loops will make your code more efficient and powerful. Practice with different data types and loop patterns to become proficient in using loops effectively.
\ No newline at end of file
+Loops are fundamental to programming in Python. Whether you're processing data, automating tasks, or building complex algorithms, mastering `for` and `while` loops will make your code more efficient and powerful. Practice with different data types and loop patterns to become proficient in using loops effectively.
+
+---
+
+## 🎯 Final Quiz: Comprehensive Loop Challenge
+
+**Question 1:** What will be the final output?
+```python
+result = 0
+for i in range(1, 5):
+ if i == 3:
+ continue
+ result += i
+print(result)
+```
+
+Show Answer
+
+**Output:** `7`
+
+**Step-by-step:**
+- i=1: result = 0 + 1 = 1
+- i=2: result = 1 + 2 = 3
+- i=3: `continue` skips this iteration
+- i=4: result = 3 + 4 = 7
+
+
+**Question 2:** Write a loop to print this pattern:
+```
+*
+**
+***
+**
+*
+```
+
+Show Answer
+
+```python
+# Growing phase
+for i in range(1, 4):
+ print("*" * i)
+
+# Shrinking phase
+for i in range(2, 0, -1):
+ print("*" * i)
+```
+
+**Alternative: Single loop approach**
+```python
+for i in [1, 2, 3, 2, 1]:
+ print("*" * i)
+```
+
+
+**Question 3:** Fix this infinite loop:
+```python
+x = 0
+while x < 10:
+ print(x)
+ if x == 5:
+ continue
+ x += 1
+```
+
+Show Answer
+
+**Problem:** When x reaches 5, `continue` skips the increment, so x stays 5 forever!
+
+**Fixed version 1: Increment before continue**
+```python
+x = 0
+while x < 10:
+ if x == 5:
+ x += 1
+ continue
+ print(x)
+ x += 1
+```
+
+**Fixed version 2: Increment at the top**
+```python
+x = 0
+while x < 10:
+ x += 1
+ if x == 5:
+ continue
+ print(x)
+```
+
+**Lesson:** Always ensure your loop variable is updated in all execution paths!
+
+
+**Question 4:** Create a function that checks if a number is prime using loops.
+
+Show Answer
+
+```python
+def is_prime(n):
+ """Check if a number is prime."""
+ if n < 2:
+ return False
+
+ # Check divisibility from 2 to sqrt(n)
+ for i in range(2, int(n ** 0.5) + 1):
+ if n % i == 0:
+ return False # Found a divisor
+
+ return True # No divisors found
+
+# Tests
+print(is_prime(7)) # True
+print(is_prime(10)) # False
+print(is_prime(13)) # True
+print(is_prime(1)) # False
+```
+
+**With else clause:**
+```python
+def is_prime(n):
+ if n < 2:
+ return False
+
+ for i in range(2, int(n ** 0.5) + 1):
+ if n % i == 0:
+ return False
+ else:
+ return True # Only executes if no break occurred
+```
+
+
+**Question 5:** What's the output of this nested loop with break?
+```python
+for i in range(3):
+ for j in range(3):
+ if i == j == 1:
+ break
+ print(f"({i}, {j})")
+```
+
+Show Answer
+
+**Output:**
+```
+(0, 0)
+(0, 1)
+(0, 2)
+(1, 0)
+(2, 0)
+(2, 1)
+(2, 2)
+```
+
+**Explanation:**
+- Outer loop i=0: prints all j values (0,1,2) → 3 prints
+- Outer loop i=1: prints j=0, then breaks when j=1 → 1 print
+- Outer loop i=2: prints all j values (0,1,2) → 3 prints
+- Total: 7 prints
+
+The `break` only exits the **inner** loop, not the outer one!
+
+
+---
+
+🎉 **Congratulations!** You've mastered Python Loops. Practice these patterns in real projects to become a loop expert!
\ No newline at end of file
diff --git a/docs/python/python-oops.md b/docs/python/python-oops.md
index d34108ad..14c038d3 100644
--- a/docs/python/python-oops.md
+++ b/docs/python/python-oops.md
@@ -61,16 +61,96 @@ p2 = Person("Bob", 30)
print(p1.greet()) # Output: Hello, my name is Alice and I am 25 years old.
print(p2.greet()) # Output: Hello, my name is Bob and I am 30 years old.
-````
+```
+---
+
+## 🎯 Quiz 1: Classes and Objects Basics
+
+**Question 1:** What's the difference between a class and an object?
+
+Show Answer
+
+- **Class:** A blueprint or template that defines the structure and behavior
+- **Object:** An actual instance created from that blueprint
+
+**Analogy:**
+- Class = Cookie cutter (blueprint)
+- Object = Actual cookie (instance)
+
+```python
+class Dog: # Blueprint
+ def bark(self):
+ return "Woof!"
+
+dog1 = Dog() # Object/Instance 1
+dog2 = Dog() # Object/Instance 2
+```
+
+You can create multiple objects from one class, each with its own data.
+
+
+**Question 2:** What is the purpose of the `__init__` method?
+
+Show Answer
+
+**`__init__`** is the **constructor method** that:
+- Automatically runs when you create a new object
+- Initializes the object's attributes
+- Sets up the initial state of the object
+
+```python
+class Student:
+ def __init__(self, name, grade):
+ self.name = name # Initialize attribute
+ self.grade = grade # Initialize attribute
+ print(f"{name} enrolled!")
+
+s1 = Student("Alice", "A") # __init__ runs automatically
+# Output: Alice enrolled!
+```
+
+**Note:** `self` refers to the instance being created.
+
+
+**Question 3:** What will be the output?
+```python
+class Counter:
+ def __init__(self):
+ self.count = 0
+
+ def increment(self):
+ self.count += 1
+c1 = Counter()
+c2 = Counter()
+c1.increment()
+c1.increment()
+c2.increment()
+print(c1.count, c2.count)
+```
+
+Show Answer
+
+**Output:** `2 1`
+
+**Explanation:**
+- Each object has its **own separate** instance attributes
+- `c1.count` is independent of `c2.count`
+- c1 was incremented twice (0 → 1 → 2)
+- c2 was incremented once (0 → 1)
+
+This demonstrates **encapsulation** - each object maintains its own state.
+
+
+---
## 🔹 Attributes and Methods
* **Attributes** → Variables inside a class.
* **Methods** → Functions defined inside a class.
-````python
+```python
class Car:
wheels = 4 # Class attribute
@@ -83,7 +163,105 @@ class Car:
c1 = Car("Tesla", "Model S")
print(c1.info()) # Tesla Model S has 4 wheels.
-````
+```
+
+---
+
+## 🎯 Quiz 2: Attributes and Methods
+
+**Question 1:** What's the difference between class attributes and instance attributes?
+
+Show Answer
+
+**Class Attributes:**
+- Shared by **all instances** of the class
+- Defined directly in the class body
+- Same value for all objects (unless overridden)
+
+**Instance Attributes:**
+- Unique to **each instance**
+- Defined in `__init__` using `self`
+- Different values for different objects
+
+```python
+class Dog:
+ species = "Canis familiaris" # Class attribute (shared)
+
+ def __init__(self, name):
+ self.name = name # Instance attribute (unique)
+
+dog1 = Dog("Buddy")
+dog2 = Dog("Max")
+
+print(dog1.species) # Canis familiaris (shared)
+print(dog2.species) # Canis familiaris (shared)
+print(dog1.name) # Buddy (unique)
+print(dog2.name) # Max (unique)
+```
+
+
+**Question 2:** What happens if you modify a class attribute?
+```python
+class Test:
+ value = 10
+
+t1 = Test()
+t2 = Test()
+Test.value = 20
+print(t1.value, t2.value)
+```
+
+Show Answer
+
+**Output:** `20 20`
+
+**Explanation:** Changing the class attribute affects **all instances** that don't have their own instance attribute with the same name.
+
+**But watch this:**
+```python
+class Test:
+ value = 10
+
+t1 = Test()
+t2 = Test()
+t1.value = 30 # Creates instance attribute for t1
+Test.value = 20 # Changes class attribute
+
+print(t1.value) # 30 (uses instance attribute)
+print(t2.value) # 20 (uses class attribute)
+print(Test.value) # 20 (class attribute)
+```
+
+Instance attributes **shadow** class attributes!
+
+
+**Question 3:** What is `self` in Python methods?
+
+Show Answer
+
+**`self`** is a reference to the **current instance** of the class. It:
+- Must be the first parameter of instance methods
+- Allows you to access instance attributes and methods
+- Is automatically passed when you call a method
+
+```python
+class Person:
+ def __init__(self, name):
+ self.name = name # self refers to the specific object
+
+ def greet(self):
+ # self allows access to this object's attributes
+ return f"Hi, I'm {self.name}"
+
+p1 = Person("Alice")
+p1.greet() # Python automatically passes p1 as self
+# Equivalent to: Person.greet(p1)
+```
+
+**Note:** You can name it anything, but `self` is the convention!
+
+
+---
## 🔹 Encapsulation
@@ -93,7 +271,7 @@ In Python, we use:
* `_protected_var` → convention for protected members
* `__private_var` → name mangling for private members
-````python
+```python
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private variable
@@ -107,7 +285,100 @@ class BankAccount:
acc = BankAccount(1000)
acc.deposit(500)
print(acc.get_balance()) # 1500
-````
+```
+
+---
+
+## 🎯 Quiz 3: Encapsulation
+
+**Question 1:** Why use private variables (with `__` prefix)?
+
+Show Answer
+
+**Private variables prevent:**
+- Accidental modification from outside the class
+- Direct access to sensitive data
+- Breaking the class's internal logic
+
+```python
+class BankAccount:
+ def __init__(self, balance):
+ self.__balance = balance
+
+ def withdraw(self, amount):
+ if amount <= self.__balance:
+ self.__balance -= amount
+ return True
+ return False
+
+acc = BankAccount(1000)
+# acc.__balance = 9999999 # Doesn't work! (name mangling)
+acc.withdraw(500) # Controlled access through method
+```
+
+This ensures **data integrity** and **controlled access**.
+
+
+**Question 2:** What happens when you try to access a private variable?
+```python
+class Secret:
+ def __init__(self):
+ self.__password = "secret123"
+
+s = Secret()
+print(s.__password)
+```
+
+Show Answer
+
+**Error:** `AttributeError: 'Secret' object has no attribute '__password'`
+
+**Explanation:** Python uses **name mangling** - it internally renames `__password` to `_Secret__password`.
+
+**You can still access it (but shouldn't!):**
+```python
+print(s._Secret__password) # secret123 (not recommended!)
+```
+
+**Proper way - use getter methods:**
+```python
+class Secret:
+ def __init__(self):
+ self.__password = "secret123"
+
+ def get_password(self):
+ return self.__password
+
+s = Secret()
+print(s.get_password()) # secret123
+```
+
+
+**Question 3:** What's the difference between `_var`, `__var`, and `var`?
+
+Show Answer
+
+| Naming Convention | Meaning | Accessible? |
+|------------------|---------|-------------|
+| `var` | Public | Yes, from anywhere |
+| `_var` | Protected (convention) | Yes, but shouldn't access from outside |
+| `__var` | Private (name mangling) | Not directly accessible |
+
+```python
+class Example:
+ def __init__(self):
+ self.public = "Everyone can access"
+ self._protected = "Please don't access directly"
+ self.__private = "Can't access easily"
+
+e = Example()
+print(e.public) # ✅ Works
+print(e._protected) # ⚠️ Works, but discouraged
+print(e.__private) # ❌ AttributeError
+```
+
+**Important:** These are mainly **conventions** (except `__var` which has actual name mangling). Python doesn't enforce strict access control like Java or C++.
+
---
@@ -115,7 +386,7 @@ print(acc.get_balance()) # 1500
Inheritance allows a class (child) to acquire properties of another class (parent).
-````python
+```python
# Base class
class Animal:
def speak(self):
@@ -134,29 +405,269 @@ d = Dog()
c = Cat()
print(d.speak()) # Woof! Woof!
print(c.speak()) # Meow!
-````
+```
+
+---
+
+## 🎯 Quiz 4: Inheritance
+
+**Question 1:** What are the benefits of inheritance?
+
+Show Answer
+
+**Benefits of Inheritance:**
+1. **Code Reusability** - Don't repeat common code
+2. **Hierarchical Organization** - Natural parent-child relationships
+3. **Extensibility** - Easy to add new features
+4. **Maintainability** - Changes in parent affect all children
+
+```python
+class Vehicle:
+ def __init__(self, brand):
+ self.brand = brand
+
+ def start(self):
+ return f"{self.brand} is starting..."
+
+# Reuse Vehicle code in Car
+class Car(Vehicle):
+ def honk(self):
+ return "Beep beep!"
+
+# Reuse Vehicle code in Motorcycle
+class Motorcycle(Vehicle):
+ def wheelie(self):
+ return "Doing a wheelie!"
+
+car = Car("Toyota")
+print(car.start()) # Inherited from Vehicle
+print(car.honk()) # Car's own method
+```
+
+
+**Question 2:** What will be the output?
+```python
+class Parent:
+ def __init__(self):
+ self.value = "Parent"
+
+ def show(self):
+ print(self.value)
+
+class Child(Parent):
+ def __init__(self):
+ super().__init__()
+ self.value = "Child"
+
+c = Child()
+c.show()
+```
+
+Show Answer
+
+**Output:** `Child`
+
+**Explanation:**
+1. `Child.__init__` is called
+2. `super().__init__()` calls `Parent.__init__`, setting `self.value = "Parent"`
+3. Then `self.value = "Child"` overwrites it
+4. `show()` is inherited from Parent, but uses Child's `value`
+
+**Without super():**
+```python
+class Child(Parent):
+ def __init__(self):
+ # No super() call!
+ self.value = "Child"
+c = Child()
+# Parent's __init__ never runs
+```
+
+
+**Question 3:** Can a child class have multiple parents in Python?
+
+Show Answer
+
+**Yes!** Python supports **multiple inheritance**.
+
+```python
+class Father:
+ def gardening(self):
+ return "I like gardening"
+
+class Mother:
+ def cooking(self):
+ return "I like cooking"
+
+class Child(Father, Mother): # Multiple inheritance
+ def playing(self):
+ return "I like playing"
+
+c = Child()
+print(c.gardening()) # From Father
+print(c.cooking()) # From Mother
+print(c.playing()) # Own method
+```
+
+**Method Resolution Order (MRO):**
+```python
+print(Child.__mro__)
+# Shows the order Python searches for methods
+```
+
+**Note:** Multiple inheritance can get complex. Use carefully!
+
+
+---
## 🔹 Polymorphism
Polymorphism allows the same method name to perform different tasks depending on the object.
-````python
+```python
for animal in [Dog(), Cat()]:
print(animal.speak())
# Output:
# Woof! Woof!
# Meow!
-````
+```
+
+---
+
+## 🎯 Quiz 5: Polymorphism
+
+**Question 1:** What is polymorphism in simple terms?
+
+Show Answer
+
+**Polymorphism** means "many forms" - the same interface (method name) can have different implementations.
+
+**Example:**
+```python
+class Shape:
+ def area(self):
+ pass
+
+class Circle(Shape):
+ def __init__(self, radius):
+ self.radius = radius
+
+ def area(self):
+ return 3.14 * self.radius ** 2
+
+class Rectangle(Shape):
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+
+ def area(self):
+ return self.width * self.height
+
+# Same method name, different behavior!
+shapes = [Circle(5), Rectangle(4, 6)]
+for shape in shapes:
+ print(shape.area()) # Polymorphism in action!
+```
+
+**Key Point:** You can treat different objects uniformly through a common interface.
+
+
+**Question 2:** What will this demonstrate?
+```python
+class Bird:
+ def move(self):
+ return "Flying"
+class Fish:
+ def move(self):
+ return "Swimming"
+
+class Snake:
+ def move(self):
+ return "Slithering"
+
+animals = [Bird(), Fish(), Snake()]
+for animal in animals:
+ print(animal.move())
+```
+
+Show Answer
+
+**Output:**
+```
+Flying
+Swimming
+Slithering
+```
+
+**This demonstrates:**
+- **Polymorphism** - Same method name (`move()`), different behaviors
+- **Duck Typing** - "If it walks like a duck and quacks like a duck, it's a duck"
+
+In Python, you don't need a common parent class. Objects just need to have the same method name!
+
+```python
+# Python doesn't care about the type, just the interface
+def make_animal_move(animal):
+ print(animal.move()) # Works for any object with move()
+
+make_animal_move(Bird()) # Flying
+make_animal_move(Fish()) # Swimming
+```
+
+
+**Question 3:** How is polymorphism different from method overriding?
+
+Show Answer
+
+**Method Overriding:**
+- A specific type of polymorphism
+- Child class redefines a parent's method
+- Requires inheritance
+
+```python
+class Animal:
+ def speak(self):
+ return "Some sound"
+
+class Dog(Animal):
+ def speak(self): # Overriding
+ return "Woof!"
+```
+
+**Polymorphism (broader concept):**
+- Different classes implementing the same interface
+- Doesn't require inheritance
+- Can work across unrelated classes
+
+```python
+# No inheritance needed!
+class Duck:
+ def speak(self):
+ return "Quack"
+
+class Person:
+ def speak(self):
+ return "Hello"
+
+# Polymorphism - same interface, different classes
+for thing in [Duck(), Person()]:
+ print(thing.speak())
+```
+
+**Summary:** Method overriding is a way to achieve polymorphism, but polymorphism is a broader concept.
+
+
+---
## 🔹 Abstraction
Abstraction means **hiding implementation details** and showing only the necessary functionality.
In Python, we use the `abc` module to create abstract classes.
-````python
+```python
from abc import ABC, abstractmethod
class Shape(ABC):
@@ -173,7 +684,135 @@ class Circle(Shape):
c = Circle(5)
print(c.area()) # 78.5
-````
+```
+
+---
+
+## 🎯 Quiz 6: Abstraction
+
+**Question 1:** Why use abstract classes?
+
+Show Answer
+
+**Abstract classes are used to:**
+1. **Define a contract** - Force child classes to implement certain methods
+2. **Prevent instantiation** - Can't create objects of abstract class
+3. **Ensure consistency** - All subclasses follow the same interface
+4. **Provide partial implementation** - Share common code while requiring specific implementations
+
+```python
+from abc import ABC, abstractmethod
+
+class PaymentMethod(ABC):
+ @abstractmethod
+ def process_payment(self, amount):
+ pass
+
+ def validate(self): # Concrete method in abstract class
+ return True
+
+class CreditCard(PaymentMethod):
+ def process_payment(self, amount):
+ return f"Processing ${amount} via Credit Card"
+
+class PayPal(PaymentMethod):
+ def process_payment(self, amount):
+ return f"Processing ${amount} via PayPal"
+
+# payment = PaymentMethod() # ❌ Error! Can't instantiate
+cc = CreditCard() # ✅ Must implement process_payment
+print(cc.process_payment(100))
+```
+
+
+**Question 2:** What happens if you don't implement an abstract method?
+```python
+from abc import ABC, abstractmethod
+
+class Animal(ABC):
+ @abstractmethod
+ def sound(self):
+ pass
+
+class Dog(Animal):
+ pass
+
+d = Dog()
+```
+
+Show Answer
+
+**Error:** `TypeError: Can't instantiate abstract class Dog with abstract method sound`
+
+**Explanation:** If a child class doesn't implement all abstract methods, it remains abstract and can't be instantiated.
+
+**Fixed version:**
+```python
+class Dog(Animal):
+ def sound(self): # Must implement
+ return "Woof!"
+
+d = Dog() # ✅ Now works
+print(d.sound())
+```
+
+**Partially implementing:**
+```python
+class Dog(Animal):
+ # Still abstract - didn't implement sound()
+ def eat(self):
+ return "Eating"
+
+# d = Dog() # ❌ Still can't instantiate
+```
+
+
+**Question 3:** What's the difference between abstraction and encapsulation?
+
+Show Answer
+
+| Concept | Purpose | Implementation |
+|---------|---------|----------------|
+| **Abstraction** | Hide complexity, show only essential features | Abstract classes, interfaces |
+| **Encapsulation** | Hide data, control access | Private variables, getter/setter |
+
+**Abstraction Example:**
+```python
+from abc import ABC, abstractmethod
+
+class Database(ABC):
+ @abstractmethod
+ def connect(self):
+ pass # Hide HOW connection works
+
+ @abstractmethod
+ def query(self, sql):
+ pass # Hide implementation details
+
+# User doesn't need to know MySQL vs PostgreSQL internals
+```
+
+**Encapsulation Example:**
+```python
+class User:
+ def __init__(self):
+ self.__password = None # Hide data
+
+ def set_password(self, pwd):
+ # Control HOW data is accessed/modified
+ if len(pwd) >= 8:
+ self.__password = pwd
+
+ def verify_password(self, pwd):
+ return self.__password == pwd
+```
+
+**Key Difference:**
+- **Abstraction** = Hiding **complexity** (what you can do)
+- **Encapsulation** = Hiding **data** (how it's stored/accessed)
+
+
+---
## 🔹 Summary
@@ -182,4 +821,266 @@ print(c.area()) # 78.5
* **Encapsulation** → Restricting access
* **Inheritance** → Reusing parent class features
* **Polymorphism** → Same function name, different behavior
-* **Abstraction** → Hiding unnecessary details
\ No newline at end of file
+* **Abstraction** → Hiding unnecessary details
+
+---
+
+## 🎯 Final Quiz: Comprehensive OOP Challenge
+
+**Question 1:** Design a simple class hierarchy for a library system.
+
+Show Answer
+
+```python
+class LibraryItem:
+ def __init__(self, title, item_id):
+ self.title = title
+ self.item_id = item_id
+ self.is_checked_out = False
+
+ def checkout(self):
+ if not self.is_checked_out:
+ self.is_checked_out = True
+ return f"{self.title} checked out"
+ return "Already checked out"
+
+ def return_item(self):
+ self.is_checked_out = False
+ return f"{self.title} returned"
+
+class Book(LibraryItem):
+ def __init__(self, title, item_id, author, pages):
+ super().__init__(title, item_id)
+ self.author = author
+ self.pages = pages
+
+ def info(self):
+ return f"Book: {self.title} by {self.author}, {self.pages} pages"
+
+class DVD(LibraryItem):
+ def __init__(self, title, item_id, director, duration):
+ super().__init__(title, item_id)
+ self.director = director
+ self.duration = duration
+
+ def info(self):
+ return f"DVD: {self.title} directed by {self.director}, {self.duration} mins"
+
+# Usage
+book = Book("Python Basics", "B001", "John Doe", 300)
+dvd = DVD("Learn Python", "D001", "Jane Smith", 120)
+
+print(book.checkout())
+print(book.info())
+```
+
+
+**Question 2:** What will be the output?
+```python
+class A:
+ def __init__(self):
+ self.x = 10
+ self.__y = 20
+
+class B(A):
+ def __init__(self):
+ super().__init__()
+ self.x = 30
+
+ def show(self):
+ print(self.x)
+ print(self.__y)
+
+b = B()
+b.show()
+```
+
+Show Answer
+
+**Output:**
+```
+30
+AttributeError: 'B' object has no attribute '_B__y'
+```
+
+**Explanation:**
+- `self.x = 30` works fine (public attribute)
+- `self.__y` doesn't exist in B's namespace due to name mangling
+- Parent's `__y` is actually named `_A__y`
+
+**Fixed version:**
+```python
+class B(A):
+ def show(self):
+ print(self.x)
+ print(self._A__y) # Access parent's private variable
+
+b = B()
+b.show()
+# Output: 30, 20
+```
+
+**Best Practice:** Use getter methods instead of accessing private variables directly!
+
+
+**Question 3:** Create a banking system with proper encapsulation.
+
+Show Answer
+
+```python
+class BankAccount:
+ def __init__(self, account_number, holder_name, initial_balance=0):
+ self.__account_number = account_number
+ self.__holder_name = holder_name
+ self.__balance = initial_balance
+ self.__transaction_history = []
+
+ def deposit(self, amount):
+ if amount > 0:
+ self.__balance += amount
+ self.__transaction_history.append(f"Deposit: +${amount}")
+ return True
+ return False
+
+ def withdraw(self, amount):
+ if 0 < amount <= self.__balance:
+ self.__balance -= amount
+ self.__transaction_history.append(f"Withdrawal: -${amount}")
+ return True
+ return False
+
+ def get_balance(self):
+ return self.__balance
+
+ def get_statement(self):
+ statement = f"Account: {self.__account_number}\n"
+ statement += f"Holder: {self.__holder_name}\n"
+ statement += f"Balance: ${self.__balance}\n"
+ statement += "Transactions:\n"
+ for transaction in self.__transaction_history:
+ statement += f" {transaction}\n"
+ return statement
+
+# Usage
+account = BankAccount("ACC123", "Alice", 1000)
+account.deposit(500)
+account.withdraw(200)
+print(account.get_statement())
+```
+
+**Key Features:**
+- All sensitive data is private (`__balance`, `__account_number`)
+- Controlled access through methods
+- Validation in deposit/withdraw
+- Transaction history tracking
+
+
+**Question 4:** Implement polymorphism for different payment methods.
+
+Show Answer
+
+```python
+class PaymentProcessor:
+ def process(self, amount):
+ raise NotImplementedError("Subclass must implement process()")
+
+class CreditCardPayment(PaymentProcessor):
+ def __init__(self, card_number):
+ self.card_number = card_number
+
+ def process(self, amount):
+ return f"Charged ${amount} to card ending in {self.card_number[-4:]}"
+
+class PayPalPayment(PaymentProcessor):
+ def __init__(self, email):
+ self.email = email
+
+ def process(self, amount):
+ return f"Sent ${amount} via PayPal to {self.email}"
+
+class BitcoinPayment(PaymentProcessor):
+ def __init__(self, wallet_address):
+ self.wallet_address = wallet_address
+
+ def process(self, amount):
+ return f"Transferred ${amount} worth of BTC to {self.wallet_address[:8]}..."
+
+# Polymorphism in action!
+def checkout(payment_method, amount):
+ print(payment_method.process(amount))
+
+# All work with the same interface
+payments = [
+ CreditCardPayment("1234567890123456"),
+ PayPalPayment("user@example.com"),
+ BitcoinPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
+]
+
+for payment in payments:
+ checkout(payment, 99.99)
+```
+
+
+**Question 5:** What OOP principles are demonstrated here?
+```python
+from abc import ABC, abstractmethod
+
+class Vehicle(ABC):
+ def __init__(self, brand):
+ self._brand = brand
+ self.__mileage = 0
+
+ @abstractmethod
+ def start(self):
+ pass
+
+ def add_miles(self, miles):
+ self.__mileage += miles
+
+ def get_mileage(self):
+ return self.__mileage
+
+class Car(Vehicle):
+ def start(self):
+ return f"{self._brand} car engine started"
+
+class Motorcycle(Vehicle):
+ def start(self):
+ return f"{self._brand} motorcycle engine started"
+```
+
+Show Answer
+
+**OOP Principles Demonstrated:**
+
+1. **Abstraction**
+ - `Vehicle` is abstract with `@abstractmethod start()`
+ - Hides implementation details
+
+2. **Encapsulation**
+ - `__mileage` is private
+ - `_brand` is protected
+ - Controlled access via `get_mileage()` and `add_miles()`
+
+3. **Inheritance**
+ - `Car` and `Motorcycle` inherit from `Vehicle`
+ - Reuse common functionality
+
+4. **Polymorphism**
+ - Both subclasses implement `start()` differently
+ - Can be used interchangeably through Vehicle interface
+
+```python
+vehicles = [Car("Toyota"), Motorcycle("Harley")]
+for vehicle in vehicles:
+ print(vehicle.start()) # Polymorphism
+ vehicle.add_miles(100) # Shared behavior
+ print(f"Mileage: {vehicle.get_mileage()}") # Encapsulation
+```
+
+**All four pillars of OOP in one example!**
+
+
+---
+
+🎉 **Congratulations!** You've mastered Object-Oriented Programming in Python. These concepts are fundamental to building scalable, maintainable applications!
\ No newline at end of file
diff --git a/docs/python/python-recursion.md b/docs/python/python-recursion.md
index 5fbcbf7f..f5a77727 100644
--- a/docs/python/python-recursion.md
+++ b/docs/python/python-recursion.md
@@ -52,7 +52,104 @@ Output:
# 2
# 1
# Time's up!
-````
+```
+
+---
+
+## 🎯 Quiz 1: Recursion Basics
+
+**Question 1:** What happens if a recursive function doesn't have a base case?
+
+Show Answer
+
+**Infinite recursion** occurs, leading to a **RecursionError** (stack overflow).
+
+```python
+def infinite_recursion(n):
+ print(n)
+ infinite_recursion(n + 1) # No base case!
+
+infinite_recursion(1)
+# Output: RecursionError: maximum recursion depth exceeded
+```
+
+**Python's default recursion limit:** ~1000 calls
+
+**Check the limit:**
+```python
+import sys
+print(sys.getrecursionlimit()) # Usually 1000
+```
+
+**Always include a base case to stop recursion!**
+
+
+**Question 2:** Identify the base case and recursive case:
+```python
+def power(base, exp):
+ if exp == 0:
+ return 1
+ return base * power(base, exp - 1)
+```
+
+Show Answer
+
+**Base Case:** `if exp == 0: return 1`
+- Stops recursion when exponent reaches 0
+- Any number to the power of 0 is 1
+
+**Recursive Case:** `return base * power(base, exp - 1)`
+- Multiplies base by the result of the same function with exp-1
+- Gradually reduces the problem size
+
+**Example trace for `power(2, 3)`:**
+```
+power(2, 3) → 2 * power(2, 2)
+ → 2 * (2 * power(2, 1))
+ → 2 * (2 * (2 * power(2, 0)))
+ → 2 * (2 * (2 * 1))
+ → 2 * (2 * 2)
+ → 2 * 4
+ → 8
+```
+
+
+**Question 3:** What will be the output?
+```python
+def mystery(n):
+ if n <= 0:
+ return
+ mystery(n - 1)
+ print(n, end=" ")
+
+mystery(4)
+```
+
+Show Answer
+
+**Output:** `1 2 3 4`
+
+**Explanation:** The print statement comes **after** the recursive call, so:
+1. `mystery(4)` calls `mystery(3)` before printing
+2. `mystery(3)` calls `mystery(2)` before printing
+3. `mystery(2)` calls `mystery(1)` before printing
+4. `mystery(1)` calls `mystery(0)` before printing
+5. `mystery(0)` hits base case, returns
+6. Now prints happen in reverse order: 1, 2, 3, 4
+
+**If print was BEFORE the recursive call:**
+```python
+def mystery(n):
+ if n <= 0:
+ return
+ print(n, end=" ") # Print first
+ mystery(n - 1)
+
+mystery(4) # Output: 4 3 2 1
+```
+
+
+---
## Factorial using Recursion
@@ -84,6 +181,114 @@ for i in range(7):
# Output: 0 1 1 2 3 5 8
```
+---
+
+## 🎯 Quiz 2: Factorial and Fibonacci
+
+**Question 1:** Trace the execution of `factorial(4)`. How many function calls are made?
+
+Show Answer
+
+**Execution trace:**
+```
+factorial(4) → 4 * factorial(3)
+ → 4 * (3 * factorial(2))
+ → 4 * (3 * (2 * factorial(1)))
+ → 4 * (3 * (2 * 1))
+ → 4 * (3 * 2)
+ → 4 * 6
+ → 24
+```
+
+**Total function calls:** 4
+- `factorial(4)`
+- `factorial(3)`
+- `factorial(2)`
+- `factorial(1)` (base case)
+
+**Pattern:** For `factorial(n)`, there are `n` function calls.
+
+
+**Question 2:** Why is the recursive Fibonacci function inefficient?
+
+Show Answer
+
+**Problem: Redundant calculations!**
+
+For `fibonacci(5)`:
+```
+ fib(5)
+ / \
+ fib(4) fib(3)
+ / \ / \
+ fib(3) fib(2) fib(2) fib(1)
+ / \ / \ / \
+ fib(2) fib(1) ...
+```
+
+**Notice:** `fib(3)` is calculated twice, `fib(2)` is calculated three times!
+
+**Time Complexity:** O(2^n) - exponential!
+
+**Solution: Use memoization or iteration**
+```python
+# Memoization
+def fib_memo(n, memo={}):
+ if n in memo:
+ return memo[n]
+ if n <= 1:
+ return n
+ memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo)
+ return memo[n]
+
+# Iterative (most efficient)
+def fib_iterative(n):
+ if n <= 1:
+ return n
+ a, b = 0, 1
+ for _ in range(2, n + 1):
+ a, b = b, a + b
+ return b
+```
+
+
+**Question 3:** Write a recursive function to calculate the sum of digits of a number.
+
+Show Answer
+
+```python
+def sum_of_digits(n):
+ """Calculate sum of digits recursively."""
+ # Base case
+ if n == 0:
+ return 0
+
+ # Recursive case
+ return (n % 10) + sum_of_digits(n // 10)
+
+# Tests
+print(sum_of_digits(123)) # 1 + 2 + 3 = 6
+print(sum_of_digits(4567)) # 4 + 5 + 6 + 7 = 22
+print(sum_of_digits(0)) # 0
+```
+
+**How it works:**
+```
+sum_of_digits(123)
+→ 3 + sum_of_digits(12)
+→ 3 + (2 + sum_of_digits(1))
+→ 3 + (2 + (1 + sum_of_digits(0)))
+→ 3 + (2 + (1 + 0))
+→ 6
+```
+
+**Explanation:**
+- `n % 10` gets the last digit
+- `n // 10` removes the last digit
+- Recursively sum remaining digits
+
+
+---
## Recursion vs Iteration
@@ -115,19 +320,302 @@ def sum_iterative(n):
print(sum_iterative(5)) # Output: 15
```
+---
+
+## 🎯 Quiz 3: Recursion vs Iteration
+
+**Question 1:** When should you choose recursion over iteration?
+
+Show Answer
+
+**Use Recursion when:**
+1. **Problem naturally recursive** (tree traversal, fractals)
+2. **Code clarity is priority** (cleaner, more readable)
+3. **Divide and conquer approach** (merge sort, quick sort)
+4. **Backtracking problems** (N-queens, maze solving)
+
+**Use Iteration when:**
+1. **Performance is critical** (faster execution)
+2. **Memory is limited** (less stack usage)
+3. **Simple sequential processing** (counting, summing)
+
+**Examples:**
+
+**Better with Recursion:**
+```python
+# Tree traversal
+def traverse_tree(node):
+ if node is None:
+ return
+ print(node.value)
+ traverse_tree(node.left)
+ traverse_tree(node.right)
+```
+
+**Better with Iteration:**
+```python
+# Simple counting
+def count_to_n(n):
+ for i in range(1, n + 1):
+ print(i)
+```
+
+
+**Question 2:** Compare the space complexity of recursive vs iterative sum:
+
+Show Answer
+
+**Recursive:**
+```python
+def sum_recursive(n):
+ if n == 0:
+ return 0
+ return n + sum_recursive(n-1)
+```
+- **Space Complexity:** O(n)
+- Each call adds a frame to the call stack
+- For `sum_recursive(1000)`, 1000 stack frames are created
+
+**Iterative:**
+```python
+def sum_iterative(n):
+ total = 0
+ for i in range(1, n+1):
+ total += i
+ return total
+```
+- **Space Complexity:** O(1)
+- Only uses a single variable `total`
+- No stack frames accumulated
+
+**Impact:**
+- Recursive uses more memory
+- May cause stack overflow for large n
+- Iterative is more memory-efficient
+
+**Check stack usage:**
+```python
+import sys
+print(sys.getrecursionlimit()) # Default: ~1000
+# sum_recursive(10000) would fail!
+```
+
+
+**Question 3:** Convert this iterative function to recursive:
+```python
+def reverse_string_iterative(s):
+ result = ""
+ for char in s:
+ result = char + result
+ return result
+```
+
+Show Answer
+
+**Recursive version:**
+```python
+def reverse_string_recursive(s):
+ # Base case: empty or single character
+ if len(s) <= 1:
+ return s
+
+ # Recursive case: first char goes to end
+ return reverse_string_recursive(s[1:]) + s[0]
+
+# Test
+print(reverse_string_recursive("hello")) # olleh
+```
+
+**How it works:**
+```
+reverse_string("hello")
+→ reverse_string("ello") + "h"
+→ (reverse_string("llo") + "e") + "h"
+→ ((reverse_string("lo") + "l") + "e") + "h"
+→ (((reverse_string("o") + "l") + "l") + "e") + "h"
+→ ((("o" + "l") + "l") + "e") + "h"
+→ "olleh"
+```
+
+**Alternative (using slicing):**
+```python
+def reverse_string(s):
+ if not s:
+ return s
+ return s[-1] + reverse_string(s[:-1])
+```
+
+
+---
## Advantages of Recursion
- Makes code **shorter and cleaner**
- Useful for problems naturally defined recursively (factorial, Fibonacci, tree traversal, divide and conquer algorithms)
+✓ Makes code **shorter and cleaner**
+✓ Useful for problems naturally defined recursively (factorial, Fibonacci, tree traversal, divide and conquer algorithms)
## Disadvantages of Recursion
- **Slower execution** than iteration (due to repeated function calls)
- **Memory usage is high** (function calls are stored in the call stack)
- Risk of **stack overflow error** if base case is missing
+✗ **Slower execution** than iteration (due to repeated function calls)
+✗ **Memory usage is high** (function calls are stored in the call stack)
+✗ Risk of **stack overflow error** if base case is missing
+
+---
+
+## 🎯 Quiz 4: Advantages and Disadvantages
+
+**Question 1:** Why is recursion slower than iteration?
+
+Show Answer
+
+**Recursion is slower because of:**
+
+1. **Function call overhead**
+ - Each call pushes a new frame onto the stack
+ - Saves parameters, local variables, return address
+ - Context switching takes time
+
+2. **Stack operations**
+ - Push and pop operations for each call
+ - More memory access = slower
+
+3. **Parameter passing**
+ - Arguments copied for each call
+
+**Comparison:**
+```python
+import time
+
+# Recursive
+def fib_recursive(n):
+ if n <= 1:
+ return n
+ return fib_recursive(n-1) + fib_recursive(n-2)
+
+# Iterative
+def fib_iterative(n):
+ if n <= 1:
+ return n
+ a, b = 0, 1
+ for _ in range(2, n + 1):
+ a, b = b, a + b
+ return b
+
+# Time comparison
+start = time.time()
+fib_recursive(30)
+print(f"Recursive: {time.time() - start:.4f}s")
+
+start = time.time()
+fib_iterative(30)
+print(f"Iterative: {time.time() - start:.4f}s")
+
+# Recursive: ~0.3s
+# Iterative: ~0.0001s
+```
+
+**Iterative is 1000x faster for Fibonacci!**
+
+
+**Question 2:** What is a stack overflow error and how do you prevent it?
+
+Show Answer
+
+**Stack Overflow Error:**
+- Occurs when the call stack exceeds its maximum size
+- In Python: `RecursionError: maximum recursion depth exceeded`
+**Causes:**
+1. Missing or incorrect base case
+2. Recursion too deep (even with correct base case)
+
+**Prevention:**
+
+**1. Ensure correct base case:**
+```python
+def countdown(n):
+ if n <= 0: # Base case
+ return
+ print(n)
+ countdown(n - 1)
+```
+
+**2. Increase recursion limit (use cautiously):**
+```python
+import sys
+sys.setrecursionlimit(10000) # Increase limit
+```
+
+**3. Convert to iteration:**
+```python
+# Instead of deep recursion
+def sum_recursive(n):
+ if n == 0:
+ return 0
+ return n + sum_recursive(n-1)
+
+# Use iteration
+def sum_iterative(n):
+ return sum(range(n + 1))
+```
+
+**4. Use tail recursion with trampoline (advanced):**
+```python
+def trampoline(f):
+ def trampolined(*args):
+ result = f(*args)
+ while callable(result):
+ result = result()
+ return result
+ return trampolined
+```
+
+
+**Question 3:** Can recursion use less memory than iteration? When?
+
+Show Answer
+
+**Generally, NO** - recursion uses more memory due to stack frames.
+
+**However, in languages with tail call optimization** (not Python), tail-recursive functions can use O(1) space.
+
+**Python doesn't optimize tail recursion, so iteration is always more memory-efficient for simple problems.**
+
+**But recursion can be clearer for complex data structures:**
+
+```python
+# Binary tree - recursion is natural
+class Node:
+ def __init__(self, value):
+ self.value = value
+ self.left = None
+ self.right = None
+
+def tree_sum_recursive(node):
+ if node is None:
+ return 0
+ return node.value + tree_sum_recursive(node.left) + tree_sum_recursive(node.right)
+
+# Iterative equivalent is much more complex
+def tree_sum_iterative(root):
+ if not root:
+ return 0
+ stack = [root]
+ total = 0
+ while stack:
+ node = stack.pop()
+ total += node.value
+ if node.right:
+ stack.append(node.right)
+ if node.left:
+ stack.append(node.left)
+ return total
+```
+
+**Verdict:** For simple problems, use iteration. For complex tree/graph structures, recursion's clarity often outweighs the memory cost.
+
+
+---
## Tail Recursion in Python
@@ -164,9 +652,363 @@ nums = [1, 3, 5, 7, 9, 11]
print(binary_search(nums, 7, 0, len(nums)-1)) # Output: 3
```
+---
+
+## 🎯 Quiz 5: Advanced Recursion
+
+**Question 1:** Trace the execution of binary search for finding 7 in `[1, 3, 5, 7, 9, 11]`:
+
+Show Answer
+
+**Array:** `[1, 3, 5, 7, 9, 11]` (indices 0-5)
+**Target:** 7
+
+**Call 1:** `binary_search(arr, 7, 0, 5)`
+- mid = (0 + 5) // 2 = 2
+- arr[2] = 5
+- 5 < 7, search right half
+
+**Call 2:** `binary_search(arr, 7, 3, 5)`
+- mid = (3 + 5) // 2 = 4
+- arr[4] = 9
+- 9 > 7, search left half
+
+**Call 3:** `binary_search(arr, 7, 3, 3)`
+- mid = (3 + 3) // 2 = 3
+- arr[3] = 7
+- Found! Return 3
+
+**Total calls:** 3
+**Time complexity:** O(log n)
+
+
+**Question 2:** Write a recursive function to calculate GCD (Greatest Common Divisor) using Euclidean algorithm.
+
+Show Answer
+
+```python
+def gcd(a, b):
+ """
+ Calculate GCD using Euclidean algorithm.
+ gcd(a, b) = gcd(b, a % b)
+ """
+ # Base case
+ if b == 0:
+ return a
+
+ # Recursive case
+ return gcd(b, a % b)
+
+# Tests
+print(gcd(48, 18)) # 6
+print(gcd(100, 35)) # 5
+print(gcd(17, 13)) # 1
+```
+
+**How it works for gcd(48, 18):**
+```
+gcd(48, 18) → gcd(18, 48 % 18) → gcd(18, 12)
+ → gcd(12, 18 % 12) → gcd(12, 6)
+ → gcd(6, 12 % 6) → gcd(6, 0)
+ → 6 (base case)
+```
+
+**Why it works:** The GCD of two numbers also divides their remainder.
+
+
+**Question 3:** Implement a recursive function to check if a string is a palindrome.
+
+Show Answer
+
+```python
+def is_palindrome(s):
+ """Check if string is palindrome recursively."""
+ # Base cases
+ if len(s) <= 1:
+ return True
+
+ # Check first and last characters
+ if s[0] != s[-1]:
+ return False
+
+ # Recursive case: check middle portion
+ return is_palindrome(s[1:-1])
+
+# Tests
+print(is_palindrome("radar")) # True
+print(is_palindrome("hello")) # False
+print(is_palindrome("a")) # True
+print(is_palindrome("racecar")) # True
+print(is_palindrome("")) # True
+```
+
+**How it works for "radar":**
+```
+is_palindrome("radar")
+→ 'r' == 'r'? Yes → is_palindrome("ada")
+ → 'a' == 'a'? Yes → is_palindrome("d")
+ → len <= 1? True
+```
+
+**Iterative comparison:**
+```python
+def is_palindrome_iterative(s):
+ return s == s[::-1] # Simpler!
+```
+
+
+---
+
## Conclusion
* Recursion is a function calling itself to solve smaller sub-problems.
* Every recursive function must have a **base case** to avoid infinite calls.
* Useful for problems like factorial, Fibonacci, searching, sorting, and tree/graph traversal.
-* While recursion makes code elegant, it may be slower and consume more memory than iteration.
\ No newline at end of file
+* While recursion makes code elegant, it may be slower and consume more memory than iteration.
+
+---
+
+## 🎯 Final Quiz: Comprehensive Recursion Challenge
+
+**Question 1:** What will be the output?
+```python
+def mystery(n):
+ if n == 0:
+ return 0
+ return n + mystery(n - 2)
+
+print(mystery(7))
+```
+
+Show Answer
+
+**Output:** `16`
+
+**Trace:**
+```
+mystery(7) → 7 + mystery(5)
+ → 7 + (5 + mystery(3))
+ → 7 + (5 + (3 + mystery(1)))
+ → 7 + (5 + (3 + (1 + mystery(-1))))
+ → 7 + (5 + (3 + (1 + (-1 + mystery(-3)))))
+ ...
+```
+
+**Wait!** This continues forever because the base case `n == 0` is never reached when starting with odd numbers!
+
+**Fixed version:**
+```python
+def mystery(n):
+ if n <= 0: # Better base case
+ return 0
+ return n + mystery(n - 2)
+
+print(mystery(7)) # 7 + 5 + 3 + 1 = 16
+```
+
+
+**Question 2:** Implement a recursive function to flatten a nested list.
+```python
+# Example: [[1, 2], [3, [4, 5]], 6] → [1, 2, 3, 4, 5, 6]
+```
+
+Show Answer
+
+```python
+def flatten(nested_list):
+ """Recursively flatten a nested list."""
+ result = []
+
+ for item in nested_list:
+ if isinstance(item, list):
+ # Recursive case: flatten sublists
+ result.extend(flatten(item))
+ else:
+ # Base case: add non-list items
+ result.append(item)
+
+ return result
+
+# Tests
+print(flatten([1, 2, 3]))
+# [1, 2, 3]
+
+print(flatten([[1, 2], [3, 4]]))
+# [1, 2, 3, 4]
+
+print(flatten([[1, 2], [3, [4, 5]], 6]))
+# [1, 2, 3, 4, 5, 6]
+
+print(flatten([[[1]], [2, [3, [4]]]]))
+# [1, 2, 3, 4]
+```
+
+**Alternative using list comprehension:**
+```python
+def flatten(nested_list):
+ return [
+ item
+ for sublist in nested_list
+ for item in (flatten(sublist) if isinstance(sublist, list) else [sublist])
+ ]
+```
+
+
+**Question 3:** Write a recursive function to generate all permutations of a string.
+
+Show Answer
+
+```python
+def permutations(s):
+ """Generate all permutations of a string."""
+ # Base case: single character
+ if len(s) <= 1:
+ return [s]
+
+ result = []
+
+ # For each character
+ for i, char in enumerate(s):
+ # Remove current character
+ remaining = s[:i] + s[i+1:]
+
+ # Get permutations of remaining characters
+ for perm in permutations(remaining):
+ # Add current character to front
+ result.append(char + perm)
+
+ return result
+
+# Tests
+print(permutations("abc"))
+# ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']
+
+print(permutations("ab"))
+# ['ab', 'ba']
+
+print(len(permutations("abcd")))
+# 24 (4! = 4 × 3 × 2 × 1)
+```
+
+**How it works for "abc":**
+1. Fix 'a': permutations of "bc" → ['bc', 'cb'] → ['abc', 'acb']
+2. Fix 'b': permutations of "ac" → ['ac', 'ca'] → ['bac', 'bca']
+3. Fix 'c': permutations of "ab" → ['ab', 'ba'] → ['cab', 'cba']
+
+
+**Question 4:** Optimize this recursive Fibonacci with memoization:
+```python
+def fib(n):
+ if n <= 1:
+ return n
+ return fib(n-1) + fib(n-2)
+```
+
+Show Answer
+
+**Method 1: Using dictionary:**
+```python
+def fib_memo(n, memo={}):
+ if n in memo:
+ return memo[n]
+
+ if n <= 1:
+ return n
+
+ memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo)
+ return memo[n]
+
+print(fib_memo(100)) # Works instantly!
+```
+
+**Method 2: Using decorator (more elegant):**
+```python
+from functools import lru_cache
+
+@lru_cache(maxsize=None)
+def fib_cached(n):
+ if n <= 1:
+ return n
+ return fib_cached(n-1) + fib_cached(n-2)
+
+print(fib_cached(100))
+```
+
+**Performance comparison:**
+```python
+# Without memoization
+fib(30) # Takes ~0.3 seconds, makes 2,692,537 calls
+
+# With memoization
+fib_memo(30) # Takes ~0.0001 seconds, makes 31 calls
+fib_memo(100) # Instant! (would be impossible without memoization)
+```
+
+**Time Complexity:**
+- Original: O(2^n) - exponential
+- Memoized: O(n) - linear
+
+
+**Question 5:** Implement the Tower of Hanoi problem recursively.
+
+Show Answer
+
+```python
+def tower_of_hanoi(n, source, destination, auxiliary):
+ """
+ Move n disks from source to destination using auxiliary rod.
+
+ Rules:
+ 1. Only one disk can be moved at a time
+ 2. A larger disk cannot be placed on a smaller disk
+ 3. All disks start on the source rod
+ """
+ # Base case: move single disk
+ if n == 1:
+ print(f"Move disk 1 from {source} to {destination}")
+ return
+
+ # Move n-1 disks from source to auxiliary (using destination)
+ tower_of_hanoi(n-1, source, auxiliary, destination)
+
+ # Move the largest disk from source to destination
+ print(f"Move disk {n} from {source} to {destination}")
+
+ # Move n-1 disks from auxiliary to destination (using source)
+ tower_of_hanoi(n-1, auxiliary, destination, source)
+
+# Test with 3 disks
+print("Tower of Hanoi with 3 disks:")
+tower_of_hanoi(3, 'A', 'C', 'B')
+```
+
+**Output:**
+```
+Move disk 1 from A to C
+Move disk 2 from A to B
+Move disk 1 from C to B
+Move disk 3 from A to C
+Move disk 1 from B to A
+Move disk 2 from B to C
+Move disk 1 from A to C
+```
+
+**Number of moves:** 2^n - 1
+- 3 disks: 7 moves
+- 4 disks: 15 moves
+- 64 disks: 18,446,744,073,709,551,615 moves (legend of Tower of Hanoi)
+
+**Visualization for 3 disks:**
+```
+Initial: Step 1: Step 2: Final:
+ | | | |
+ -|- -|- | -|-
+--|-- | -|- --|--
+----|-- | --|-- ----|--
+ A A B C
+```
+
+
+---
+
+🎉 **Congratulations!** You've mastered Recursion in Python. Practice with tree traversals, backtracking, and divide-and-conquer algorithms to become a recursion expert!
\ No newline at end of file
diff --git a/docs/python/python-set.md b/docs/python/python-set.md
index b53020fb..5484b117 100644
--- a/docs/python/python-set.md
+++ b/docs/python/python-set.md
@@ -40,6 +40,59 @@ Sets are commonly used when:
- **Mutable:** You can add or remove items.
- **Iterable:** You can loop through a set.
+---
+
+## 🎯 Quiz 1: Set Basics and Characteristics
+
+**Question 1:** What happens when you try to create a set with duplicate values?
+```python
+numbers = {1, 2, 2, 3, 3, 3, 4}
+print(numbers)
+```
+
+Show Answer
+
+**Output:** `{1, 2, 3, 4}`
+
+**Explanation:** Sets automatically remove duplicates. Only unique values are stored, which is one of the main features of sets.
+
+
+**Question 2:** What's the difference between `{}` and `set()`?
+
+Show Answer
+
+- **`{}`** creates an **empty dictionary**, not a set
+- **`set()`** creates an **empty set**
+
+```python
+empty_dict = {}
+empty_set = set()
+
+print(type(empty_dict)) #
+print(type(empty_set)) #
+```
+
+**Common mistake:** Using `{}` when you mean to create an empty set!
+
+
+**Question 3:** Can you access set elements by index like `my_set[0]`?
+
+Show Answer
+
+**No!** Sets are **unordered**, so they don't support indexing or slicing.
+
+```python
+fruits = {"apple", "banana", "cherry"}
+# fruits[0] # TypeError: 'set' object is not subscriptable
+```
+
+**To access elements:**
+- Use loops: `for fruit in fruits:`
+- Check membership: `if "apple" in fruits:`
+- Convert to list: `list(fruits)[0]` (but order is not guaranteed)
+
+
+---
## Creating a Set
@@ -50,7 +103,7 @@ There are two main ways to create a set:
```python
fruits = {"apple", "banana", "cherry"}
print(fruits) # Output: {'banana', 'apple', 'cherry'}
-````
+```
### Using the `set()` Constructor
@@ -128,6 +181,70 @@ colors.clear()
print(colors) # Output: set()
```
+---
+
+## 🎯 Quiz 2: Adding and Removing Elements
+
+**Question 1:** What's the difference between `remove()` and `discard()`?
+
+Show Answer
+
+**`remove(item)`:**
+- Raises a `KeyError` if the item doesn't exist
+- Use when you're sure the item exists
+
+```python
+s = {1, 2, 3}
+s.remove(4) # KeyError: 4
+```
+
+**`discard(item)`:**
+- Does NOT raise an error if the item doesn't exist
+- Use when you're unsure if the item exists (safer)
+
+```python
+s = {1, 2, 3}
+s.discard(4) # No error, silently does nothing
+```
+
+**Best practice:** Use `discard()` for safer code unless you specifically need the error notification.
+
+
+**Question 2:** What will happen?
+```python
+my_set = {1, 2, 3}
+my_set.add(2)
+print(my_set)
+```
+
+Show Answer
+
+**Output:** `{1, 2, 3}`
+
+**Explanation:** Adding an element that already exists does nothing. Sets only store unique values, so duplicate additions are ignored without error.
+
+
+**Question 3:** What makes `pop()` unpredictable for sets?
+
+Show Answer
+
+**`pop()` removes and returns an arbitrary (random) element** because sets are unordered.
+
+```python
+s = {1, 2, 3, 4, 5}
+print(s.pop()) # Could be any element!
+print(s.pop()) # Could be any remaining element!
+```
+
+**For lists:** `pop()` removes the last element (predictable)
+**For sets:** `pop()` removes an arbitrary element (unpredictable)
+
+**Use case:** When you need to remove elements but don't care which one.
+
+**Caution:** `pop()` on an empty set raises `KeyError`!
+
+
+---
## Updating a Set
@@ -150,17 +267,76 @@ print(2 in nums) # Output: True
print(5 not in nums) # Output: True
```
+---
+
+## 🎯 Quiz 3: Set Operations and Membership
+
+**Question 1:** Why is membership testing faster in sets than in lists?
+
+Show Answer
+
+**Sets use hash tables** for O(1) average-case lookup time.
+
+**Performance comparison:**
+```python
+# Set - O(1) average case
+my_set = {1, 2, 3, ..., 1000000}
+print(999999 in my_set) # Very fast!
+
+# List - O(n) worst case
+my_list = [1, 2, 3, ..., 1000000]
+print(999999 in my_list) # Must check each element!
+```
+
+**Rule of thumb:**
+- Use **sets** for frequent membership tests
+- Use **lists** when you need ordering or duplicates
+
+**Example use case:**
+```python
+# Check if email is in blacklist (thousands of emails)
+blacklist = set(["spam@example.com", ...]) # Fast!
+if user_email in blacklist:
+ reject()
+```
+
+
+**Question 2:** What will `update()` do with duplicate values?
+```python
+s = {1, 2, 3}
+s.update([2, 3, 4, 5])
+print(s)
+```
+
+Show Answer
+
+**Output:** `{1, 2, 3, 4, 5}`
+
+**Explanation:**
+- `update()` adds all unique elements from the iterable
+- Duplicates (2 and 3) are automatically ignored
+- Only new unique values (4 and 5) are added
+
+**`update()` accepts multiple iterables:**
+```python
+s = {1}
+s.update([2, 3], {4, 5}, (6, 7))
+print(s) # {1, 2, 3, 4, 5, 6, 7}
+```
+
+
+---
## Common Set Operations
Python sets support powerful operations:
-| Operation | Syntax | Description | |
-| -------------------- | -------------------------------------- | ------------------------------------ | ----------------------------------- |
-| Union | \`a | b`or`a.union(b)\` | Combine all elements from both sets |
-| Intersection | `a & b` or `a.intersection(b)` | Elements common to both sets | |
-| Difference | `a - b` or `a.difference(b)` | Elements in `a` but not in `b` | |
-| Symmetric Difference | `a ^ b` or `a.symmetric_difference(b)` | Elements in either set, but not both | |
+| Operation | Syntax | Description |
+| -------------------- | -------------------------------------- | ------------------------------------ |
+| Union | `a | b` or `a.union(b)` | Combine all elements from both sets |
+| Intersection | `a & b` or `a.intersection(b)` | Elements common to both sets |
+| Difference | `a - b` or `a.difference(b)` | Elements in `a` but not in `b` |
+| Symmetric Difference | `a ^ b` or `a.symmetric_difference(b)` | Elements in either set, but not both |
---
@@ -185,6 +361,112 @@ print(a ^ b) # {1, 2, 4, 5}
---
+## 🎯 Quiz 4: Set Operations
+
+**Question 1:** Explain the difference between these operations:
+```python
+a = {1, 2, 3, 4}
+b = {3, 4, 5, 6}
+
+print(a - b) # ?
+print(b - a) # ?
+print(a ^ b) # ?
+```
+
+Show Answer
+
+**Outputs:**
+```python
+print(a - b) # {1, 2} - elements in a but NOT in b
+print(b - a) # {5, 6} - elements in b but NOT in a
+print(a ^ b) # {1, 2, 5, 6} - elements in either, but NOT both
+```
+
+**Visual representation:**
+```
+a = {1, 2, 3, 4}
+b = {3, 4, 5, 6}
+
+a - b: [1, 2] (3, 4)
+b - a: (3, 4) [5, 6]
+a ^ b: [1, 2] [5, 6] (symmetric difference)
+a & b: (3, 4) (intersection)
+a | b: [1, 2] (3, 4) [5, 6] (union)
+```
+
+**Note:** `a - b` ≠ `b - a` (difference is NOT commutative)
+
+
+**Question 2:** What's the practical difference between using operators (`|`, `&`) vs methods (`.union()`, `.intersection()`)?
+
+Show Answer
+
+**Operators (`|`, `&`, `-`, `^`):**
+- Only work with sets
+- Cleaner, more mathematical syntax
+- Raise `TypeError` if used with non-sets
+
+```python
+a = {1, 2, 3}
+b = {3, 4, 5}
+print(a | b) # Works
+
+# a | [3, 4, 5] # TypeError: unsupported operand type(s)
+```
+
+**Methods (`.union()`, `.intersection()`, etc.):**
+- Accept any iterable (lists, tuples, sets)
+- More flexible
+- Slightly more verbose
+
+```python
+a = {1, 2, 3}
+print(a.union([3, 4, 5])) # Works! {1, 2, 3, 4, 5}
+print(a.union([6], {7})) # Multiple iterables! {1, 2, 3, 6, 7}
+```
+
+**Best practice:**
+- Use operators for set-to-set operations
+- Use methods when working with mixed types
+
+
+**Question 3:** Find common elements in three sets:
+```python
+set1 = {1, 2, 3, 4, 5}
+set2 = {3, 4, 5, 6, 7}
+set3 = {5, 6, 7, 8, 9}
+```
+
+Show Answer
+
+**Solution:**
+```python
+common = set1 & set2 & set3
+print(common) # {5}
+
+# Or using method
+common = set1.intersection(set2, set3)
+print(common) # {5}
+```
+
+**Step by step:**
+1. `set1 & set2` → `{3, 4, 5}` (common in first two)
+2. `{3, 4, 5} & set3` → `{5}` (common in all three)
+
+**Real-world example:**
+```python
+# Students who took all three courses
+python_students = {"Alice", "Bob", "Charlie", "David"}
+java_students = {"Bob", "Charlie", "Eve"}
+cpp_students = {"Charlie", "Frank"}
+
+all_three = python_students & java_students & cpp_students
+print(all_three) # {'Charlie'}
+```
+
+
+---
+
## Iterating Over a Set
Loop through elements with a `for` loop:
@@ -211,6 +493,115 @@ print(fs) # frozenset({1, 2, 3})
Use `frozenset` when you need a **hashable set**, e.g., as dictionary keys.
+---
+
+## 🎯 Quiz 5: Frozenset and Advanced Concepts
+
+**Question 1:** Why would you use a frozenset instead of a regular set?
+
+Show Answer
+
+**Use frozenset when you need:**
+
+1. **Immutability** (cannot be changed after creation)
+```python
+fs = frozenset([1, 2, 3])
+# fs.add(4) # AttributeError - cannot modify
+```
+
+2. **Hashable sets** (can be used as dictionary keys or set elements)
+```python
+# Regular sets cannot be dictionary keys
+# my_dict = {{1, 2}: "value"} # TypeError
+
+# Frozensets can!
+my_dict = {frozenset([1, 2]): "value"}
+print(my_dict) # {frozenset({1, 2}): 'value'}
+
+# Sets of sets (using frozenset)
+set_of_sets = {frozenset([1, 2]), frozenset([3, 4])}
+print(set_of_sets)
+```
+
+3. **Thread-safety** (immutable objects are inherently thread-safe)
+
+**Trade-off:** Cannot modify, but gain hashability and safety.
+
+
+**Question 2:** Can you convert between set and frozenset?
+
+Show Answer
+
+**Yes!** You can convert in both directions:
+
+```python
+# Set to frozenset
+regular_set = {1, 2, 3}
+frozen = frozenset(regular_set)
+print(frozen) # frozenset({1, 2, 3})
+
+# Frozenset to set
+frozen_set = frozenset([4, 5, 6])
+regular = set(frozen_set)
+print(regular) # {4, 5, 6}
+
+# Now you can modify the regular set
+regular.add(7)
+print(regular) # {4, 5, 6, 7}
+```
+
+**Use case:**
+```python
+# Store sets as dictionary keys
+cache = {}
+key = frozenset(["param1", "param2"])
+cache[key] = "result"
+
+# Convert back to set for modifications
+params = set(key)
+params.add("param3")
+```
+
+
+**Question 3:** What operations can you perform on frozensets?
+
+Show Answer
+
+**Frozensets support all READ operations but NO WRITE operations:**
+
+**✅ Works:**
+```python
+fs1 = frozenset([1, 2, 3])
+fs2 = frozenset([3, 4, 5])
+
+# Set operations
+print(fs1 | fs2) # Union: frozenset({1, 2, 3, 4, 5})
+print(fs1 & fs2) # Intersection: frozenset({3})
+print(fs1 - fs2) # Difference: frozenset({1, 2})
+print(fs1 ^ fs2) # Symmetric difference: frozenset({1, 2, 4, 5})
+
+# Membership
+print(2 in fs1) # True
+
+# Iteration
+for item in fs1:
+ print(item)
+```
+
+**❌ Doesn't work:**
+```python
+fs = frozenset([1, 2, 3])
+# fs.add(4) # AttributeError
+# fs.remove(1) # AttributeError
+# fs.update([4, 5]) # AttributeError
+# fs.discard(1) # AttributeError
+# fs.clear() # AttributeError
+```
+
+**Summary:** You can query and combine frozensets, but not modify them.
+
+
+---
## When to Use Sets?
@@ -237,6 +628,260 @@ Use `frozenset` when you need a **hashable set**, e.g., as dictionary keys.
---
+## 🎯 Final Quiz: Comprehensive Set Challenge
+
+**Question 1:** Remove duplicates from a list while preserving order:
+```python
+numbers = [1, 3, 2, 3, 4, 1, 5, 2]
+# Convert to unique list
+```
+
+Show Answer
+
+**Problem:** Sets don't preserve order!
+
+```python
+# ❌ Wrong - loses order
+numbers = [1, 3, 2, 3, 4, 1, 5, 2]
+unique = list(set(numbers))
+print(unique) # Order not preserved!
+```
+
+**✅ Correct solutions:**
+
+**Method 1: Using dict (Python 3.7+)**
+```python
+numbers = [1, 3, 2, 3, 4, 1, 5, 2]
+unique = list(dict.fromkeys(numbers))
+print(unique) # [1, 3, 2, 4, 5]
+```
+
+**Method 2: Manual with set for tracking**
+```python
+numbers = [1, 3, 2, 3, 4, 1, 5, 2]
+seen = set()
+unique = []
+for num in numbers:
+ if num not in seen:
+ seen.add(num)
+ unique.append(num)
+print(unique) # [1, 3, 2, 4, 5]
+```
+
+**Method 3: List comprehension (one-liner)**
+```python
+numbers = [1, 3, 2, 3, 4, 1, 5, 2]
+seen = set()
+unique = [x for x in numbers if not (x in seen or seen.add(x))]
+print(unique) # [1, 3, 2, 4, 5]
+```
+
+
+**Question 2:** Find students who are in at least 2 out of 3 clubs:
+```python
+chess_club = {"Alice", "Bob", "Charlie"}
+drama_club = {"Bob", "Diana", "Eve"}
+sports_club = {"Alice", "Bob", "Frank"}
+```
+
+Show Answer
+
+```python
+chess_club = {"Alice", "Bob", "Charlie"}
+drama_club = {"Bob", "Diana", "Eve"}
+sports_club = {"Alice", "Bob", "Frank"}
+
+# Students in exactly 2 clubs
+in_two = ((chess_club & drama_club) |
+ (drama_club & sports_club) |
+ (chess_club & sports_club)) - (chess_club & drama_club & sports_club)
+
+# Students in all 3 clubs
+in_three = chess_club & drama_club & sports_club
+
+# Students in at least 2 clubs
+in_at_least_two = in_two | in_three
+
+print("In at least 2 clubs:", in_at_least_two) # {'Alice', 'Bob'}
+
+# Alternative approach - count membership
+all_students = chess_club | drama_club | sports_club
+result = set()
+
+for student in all_students:
+ count = 0
+ if student in chess_club:
+ count += 1
+ if student in drama_club:
+ count += 1
+ if student in sports_club:
+ count += 1
+
+ if count >= 2:
+ result.add(student)
+
+print("In at least 2 clubs:", result) # {'Alice', 'Bob'}
+```
+
+
+**Question 3:** Implement a function to check if two sets are disjoint (have no common elements):
+
+Show Answer
+
+```python
+def are_disjoint(set1, set2):
+ """Check if two sets have no common elements."""
+ return len(set1 & set2) == 0
+
+# Alternative using built-in method
+def are_disjoint_v2(set1, set2):
+ return set1.isdisjoint(set2)
+
+# Tests
+a = {1, 2, 3}
+b = {4, 5, 6}
+c = {3, 4, 5}
+
+print(are_disjoint(a, b)) # True (no overlap)
+print(are_disjoint(a, c)) # False (3 is common)
+
+# The built-in way (most efficient)
+print(a.isdisjoint(b)) # True
+print(a.isdisjoint(c)) # False
+```
+
+**Other useful set comparison methods:**
+```python
+a = {1, 2, 3}
+b = {1, 2, 3, 4, 5}
+
+print(a.issubset(b)) # True - is a contained in b?
+print(b.issuperset(a)) # True - does b contain a?
+print(a == b) # False - are they equal?
+```
+
+
+**Question 4:** Use sets to find anagrams:
+```python
+# Check if two words are anagrams
+word1 = "listen"
+word2 = "silent"
+```
+
+Show Answer
+
+```python
+def are_anagrams(word1, word2):
+ """Check if two words are anagrams using sets."""
+ # Method 1: Check if they have the same characters
+ # Note: This checks unique characters only!
+ return set(word1.lower()) == set(word2.lower())
+
+# Problem: This doesn't account for frequency!
+print(are_anagrams("listen", "silent")) # True
+print(are_anagrams("aab", "abb")) # True (Wrong! Different letter counts)
+
+# Better solution using sorted
+def are_anagrams_correct(word1, word2):
+ return sorted(word1.lower()) == sorted(word2.lower())
+
+print(are_anagrams_correct("listen", "silent")) # True
+print(are_anagrams_correct("aab", "abb")) # False (Correct!)
+
+# Using Counter (best for frequency-aware comparison)
+from collections import Counter
+
+def are_anagrams_best(word1, word2):
+ return Counter(word1.lower()) == Counter(word2.lower())
+
+# Group anagrams from a list
+words = ["eat", "tea", "tan", "ate", "nat", "bat"]
+anagram_groups = {}
+
+for word in words:
+ key = ''.join(sorted(word))
+ if key not in anagram_groups:
+ anagram_groups[key] = []
+ anagram_groups[key].append(word)
+
+print(anagram_groups)
+# {'aet': ['eat', 'tea', 'ate'], 'ant': ['tan', 'nat'], 'abt': ['bat']}
+```
+
+
+**Question 5:** Implement set-based caching for expensive function calls:
+
+Show Answer
+
+```python
+class ComputationCache:
+ """Cache expensive computations using sets and dicts."""
+
+ def __init__(self):
+ self.computed = set() # Track what we've computed
+ self.cache = {} # Store results
+
+ def compute(self, params):
+ """Simulate expensive computation."""
+ # Convert to frozenset for hashability
+ key = frozenset(params.items()) if isinstance(params, dict) else frozenset(params)
+
+ # Check if already computed
+ if key in self.computed:
+ print(f"Cache hit for {params}")
+ return self.cache[key]
+
+ # Perform expensive computation
+ print(f"Computing for {params}...")
+ result = sum(params.values()) if isinstance(params, dict) else sum(params)
+
+ # Store in cache
+ self.computed.add(key)
+ self.cache[key] = result
+
+ return result
+
+# Usage
+cache = ComputationCache()
+
+params1 = {"a": 1, "b": 2, "c": 3}
+print(cache.compute(params1)) # Computing... 6
+print(cache.compute(params1)) # Cache hit! 6
+
+params2 = [1, 2, 3, 4, 5]
+print(cache.compute(params2)) # Computing... 15
+print(cache.compute(params2)) # Cache hit! 15
+
+# Check what's been computed
+print(f"Computed {len(cache.computed)} unique parameter sets")
+```
+
+**Real-world application:**
+```python
+# User permissions system
+class PermissionChecker:
+ def __init__(self):
+ self.admin_users = {"alice", "bob"}
+ self.moderator_users = {"charlie", "diana"}
+ self.banned_users = {"eve"}
+
+ def can_post(self, username):
+ if username in self.banned_users:
+ return False
+ return username in (self.admin_users | self.moderator_users)
+
+ def is_admin(self, username):
+ return username in self.admin_users
+
+checker = PermissionChecker()
+print(checker.can_post("alice")) # True
+print(checker.can_post("eve")) # False
+print(checker.is_admin("charlie")) # False
+```
+
+
+---
+
## Summary Table
| Feature | Description |
@@ -248,3 +893,6 @@ Use `frozenset` when you need a **hashable set**, e.g., as dictionary keys.
| **Immutable Variant** | `frozenset` |
| **Typical Use Cases** | Membership tests, uniqueness, set operations |
+---
+
+🎉 **Congratulations!** You've mastered Sets in Python. Practice with real-world problems like duplicate removal, membership testing, and set algebra to strengthen your skills!
\ No newline at end of file
diff --git a/docs/python/python-simple-project.md b/docs/python/python-simple-project.md
new file mode 100644
index 00000000..9cba450f
--- /dev/null
+++ b/docs/python/python-simple-project.md
@@ -0,0 +1,990 @@
+---
+id: python-simple-projects
+title: Python Simple Projects
+sidebar_label: Simple Projects
+sidebar_position: 20
+tags:
+ [
+ Python,
+ Projects,
+ Beginner Projects,
+ Practice,
+ Hands-on Learning,
+ Python Applications
+ ]
+
+---
+
+# Python Simple Projects
+
+Building projects is the best way to learn Python! This guide contains beginner-friendly projects that help you practice core Python concepts while creating something fun and useful.
+
+Each project includes:
+* **Concept Overview**: What you'll learn
+* **Complete Code**: Full working implementation
+* **Explanation**: How the code works
+* **Extensions**: Ideas to improve the project
+
+
+## Why Build Projects?
+
+* **Practical Learning**: Apply concepts in real scenarios
+* **Portfolio Building**: Showcase your skills
+* **Problem Solving**: Develop logical thinking
+* **Confidence**: See your code come to life
+* **Fun**: Learn by creating something interesting!
+
+---
+
+## Project 1: Number Guessing Game
+
+### 🎯 Concepts Covered:
+* Random number generation
+* While loops
+* Conditional statements
+* User input handling
+
+### 📝 Project Description:
+Create a game where the computer picks a random number and the player has to guess it. The program provides hints (too high/too low) until the player guesses correctly.
+
+### 💻 Complete Code:
+
+```python
+import random
+
+def number_guessing_game():
+ """
+ A simple number guessing game
+ """
+ # Generate random number between 1 and 100
+ secret_number = random.randint(1, 100)
+ attempts = 0
+ max_attempts = 10
+
+ print("🎮 Welcome to the Number Guessing Game!")
+ print(f"I'm thinking of a number between 1 and 100.")
+ print(f"You have {max_attempts} attempts to guess it.\n")
+
+ while attempts < max_attempts:
+ try:
+ # Get user input
+ guess = int(input(f"Attempt {attempts + 1}/{max_attempts} - Enter your guess: "))
+ attempts += 1
+
+ # Check the guess
+ if guess < secret_number:
+ print("📈 Too low! Try a higher number.\n")
+ elif guess > secret_number:
+ print("📉 Too high! Try a lower number.\n")
+ else:
+ print(f"\n🎉 Congratulations! You guessed it in {attempts} attempts!")
+ print(f"The number was {secret_number}")
+ return
+
+ except ValueError:
+ print("❌ Please enter a valid number!\n")
+
+ print(f"\n😞 Game Over! You've used all {max_attempts} attempts.")
+ print(f"The number was {secret_number}")
+
+# Run the game
+if __name__ == "__main__":
+ number_guessing_game()
+```
+
+### 🔍 How It Works:
+
+1. **Random Number**: `random.randint(1, 100)` generates a random number
+2. **Loop**: While loop continues until player guesses or runs out of attempts
+3. **Validation**: Try-except block handles invalid inputs
+4. **Feedback**: Conditional statements provide hints
+5. **Win Condition**: Exact match ends the game successfully
+
+### 🚀 Extensions:
+
+* Add difficulty levels (Easy: 1-50, Hard: 1-1000)
+* Track high scores
+* Add a play again option
+* Implement a two-player mode
+* Add a scoring system based on attempts
+
+---
+
+## 📝 Quiz 1: Number Guessing Game
+
+**Q1. Which module is used to generate random numbers?**
+- A) math
+- B) random
+- C) numbers
+- D) generate
+
+
+Show Answer
+**Answer: B) random**
+
+The `random` module provides functions for generating random numbers, including `randint()` for random integers.
+
+
+**Q2. What does `random.randint(1, 100)` do?**
+- A) Generates a random float between 1 and 100
+- B) Generates a random integer between 1 and 99
+- C) Generates a random integer between 1 and 100 (inclusive)
+- D) Generates 100 random numbers
+
+
+Show Answer
+**Answer: C) Generates a random integer between 1 and 100 (inclusive)**
+
+`randint(a, b)` returns a random integer N such that a <= N <= b, so both endpoints are included.
+
+
+---
+
+## Project 2: To-Do List Application
+
+### 🎯 Concepts Covered:
+* Lists and list methods
+* Functions
+* File handling (optional)
+* Menu-driven program
+
+### 📝 Project Description:
+A command-line to-do list application that allows users to add, view, remove, and mark tasks as complete.
+
+### 💻 Complete Code:
+
+```python
+def display_menu():
+ """Display the menu options"""
+ print("\n" + "="*40)
+ print("📝 TO-DO LIST MANAGER")
+ print("="*40)
+ print("1. View Tasks")
+ print("2. Add Task")
+ print("3. Remove Task")
+ print("4. Mark Task as Complete")
+ print("5. Exit")
+ print("="*40)
+
+def view_tasks(tasks):
+ """Display all tasks"""
+ if not tasks:
+ print("\n✨ No tasks yet! You're all caught up!")
+ return
+
+ print("\n📋 Your Tasks:")
+ print("-" * 40)
+ for index, task in enumerate(tasks, 1):
+ status = "✅" if task['completed'] else "⭕"
+ print(f"{index}. {status} {task['name']}")
+ print("-" * 40)
+
+def add_task(tasks):
+ """Add a new task"""
+ task_name = input("\n➕ Enter task name: ").strip()
+ if task_name:
+ tasks.append({'name': task_name, 'completed': False})
+ print(f"✅ Task '{task_name}' added successfully!")
+ else:
+ print("❌ Task name cannot be empty!")
+
+def remove_task(tasks):
+ """Remove a task"""
+ view_tasks(tasks)
+ if not tasks:
+ return
+
+ try:
+ task_num = int(input("\n🗑️ Enter task number to remove: "))
+ if 1 <= task_num <= len(tasks):
+ removed = tasks.pop(task_num - 1)
+ print(f"✅ Task '{removed['name']}' removed!")
+ else:
+ print("❌ Invalid task number!")
+ except ValueError:
+ print("❌ Please enter a valid number!")
+
+def mark_complete(tasks):
+ """Mark a task as complete"""
+ view_tasks(tasks)
+ if not tasks:
+ return
+
+ try:
+ task_num = int(input("\n✔️ Enter task number to mark complete: "))
+ if 1 <= task_num <= len(tasks):
+ tasks[task_num - 1]['completed'] = True
+ print(f"✅ Task '{tasks[task_num - 1]['name']}' marked as complete!")
+ else:
+ print("❌ Invalid task number!")
+ except ValueError:
+ print("❌ Please enter a valid number!")
+
+def todo_list_app():
+ """Main application function"""
+ tasks = []
+
+ print("🌟 Welcome to Your To-Do List Manager! 🌟")
+
+ while True:
+ display_menu()
+ choice = input("\n👉 Enter your choice (1-5): ")
+
+ if choice == '1':
+ view_tasks(tasks)
+ elif choice == '2':
+ add_task(tasks)
+ elif choice == '3':
+ remove_task(tasks)
+ elif choice == '4':
+ mark_complete(tasks)
+ elif choice == '5':
+ print("\n👋 Thanks for using To-Do List Manager! Goodbye!")
+ break
+ else:
+ print("❌ Invalid choice! Please select 1-5.")
+
+# Run the application
+if __name__ == "__main__":
+ todo_list_app()
+```
+
+### 🔍 How It Works:
+
+1. **Data Structure**: List of dictionaries storing task name and completion status
+2. **Menu System**: While loop displays menu until user exits
+3. **Functions**: Separate functions for each operation (clean code)
+4. **Validation**: Error handling for invalid inputs
+5. **User-Friendly**: Emojis and clear messages enhance experience
+
+### 🚀 Extensions:
+
+* Save tasks to a file (persistence)
+* Add task priorities (High, Medium, Low)
+* Add due dates for tasks
+* Sort tasks by priority or date
+* Add categories/tags to tasks
+
+---
+
+## 📝 Quiz 2: To-Do List Application
+
+**Q1. What data structure is best for storing a collection of tasks?**
+- A) Integer
+- B) String
+- C) List
+- D) Boolean
+
+
+Show Answer
+**Answer: C) List**
+
+Lists are perfect for storing collections of items that can grow or shrink, like a to-do list.
+
+
+**Q2. How do you remove an item at a specific index from a list?**
+- A) `list.delete(index)`
+- B) `list.remove(index)`
+- C) `list.pop(index)`
+- D) `del list(index)`
+
+
+Show Answer
+**Answer: C) `list.pop(index)`**
+
+The `pop()` method removes and returns the item at the given index. You can also use `del list[index]`.
+
+
+---
+
+## Project 3: Simple Calculator
+
+### 🎯 Concepts Covered:
+* Functions
+* Arithmetic operators
+* User input
+* Error handling
+
+### 📝 Project Description:
+A calculator that performs basic arithmetic operations with a user-friendly interface.
+
+### 💻 Complete Code:
+
+```python
+def add(x, y):
+ """Addition"""
+ return x + y
+
+def subtract(x, y):
+ """Subtraction"""
+ return x - y
+
+def multiply(x, y):
+ """Multiplication"""
+ return x * y
+
+def divide(x, y):
+ """Division"""
+ if y == 0:
+ return "Error: Division by zero!"
+ return x / y
+
+def power(x, y):
+ """Exponentiation"""
+ return x ** y
+
+def modulus(x, y):
+ """Modulus"""
+ if y == 0:
+ return "Error: Modulus by zero!"
+ return x % y
+
+def calculator():
+ """Main calculator function"""
+ print("🔢 " + "="*40)
+ print(" SIMPLE CALCULATOR")
+ print("="*40)
+
+ while True:
+ print("\n📐 Select Operation:")
+ print("1. ➕ Addition")
+ print("2. ➖ Subtraction")
+ print("3. ✖️ Multiplication")
+ print("4. ➗ Division")
+ print("5. 🔺 Power")
+ print("6. 📊 Modulus")
+ print("7. 🚪 Exit")
+ print("-" * 40)
+
+ choice = input("👉 Enter choice (1-7): ")
+
+ if choice == '7':
+ print("\n👋 Thank you for using Calculator! Goodbye!")
+ break
+
+ if choice in ['1', '2', '3', '4', '5', '6']:
+ try:
+ num1 = float(input("Enter first number: "))
+ num2 = float(input("Enter second number: "))
+
+ if choice == '1':
+ result = add(num1, num2)
+ operation = "+"
+ elif choice == '2':
+ result = subtract(num1, num2)
+ operation = "-"
+ elif choice == '3':
+ result = multiply(num1, num2)
+ operation = "×"
+ elif choice == '4':
+ result = divide(num1, num2)
+ operation = "÷"
+ elif choice == '5':
+ result = power(num1, num2)
+ operation = "^"
+ else: # choice == '6'
+ result = modulus(num1, num2)
+ operation = "%"
+
+ print(f"\n✅ Result: {num1} {operation} {num2} = {result}")
+
+ except ValueError:
+ print("\n❌ Invalid input! Please enter valid numbers.")
+ else:
+ print("\n❌ Invalid choice! Please select 1-7.")
+
+ input("\nPress Enter to continue...")
+
+# Run the calculator
+if __name__ == "__main__":
+ calculator()
+```
+
+### 🔍 How It Works:
+
+1. **Functions**: Separate function for each operation (modular design)
+2. **Menu Loop**: Continuous operation until user exits
+3. **Type Conversion**: `float()` allows decimal numbers
+4. **Error Handling**: Try-except for invalid inputs, if statements for division by zero
+5. **User Experience**: Clear prompts and formatted output
+
+### 🚀 Extensions:
+
+* Add more operations (square root, factorial, logarithm)
+* Add memory functions (store/recall results)
+* Implement calculation history
+* Add scientific calculator mode
+* Support complex mathematical expressions
+
+---
+
+## 📝 Quiz 3: Calculator Project
+
+**Q1. What happens if you try to divide by zero in Python without error handling?**
+- A) Returns 0
+- B) Returns None
+- C) Raises ZeroDivisionError
+- D) Returns infinity
+
+
+Show Answer
+**Answer: C) Raises ZeroDivisionError**
+
+Dividing by zero raises a `ZeroDivisionError` exception if not handled properly.
+
+
+**Q2. Why is it better to create separate functions for each operation?**
+- A) It makes the code longer
+- B) It makes the code modular, reusable, and easier to maintain
+- C) It's required by Python
+- D) It makes the code run faster
+
+
+Show Answer
+**Answer: B) It makes the code modular, reusable, and easier to maintain**
+
+Breaking code into functions improves readability, reusability, and makes debugging easier.
+
+
+---
+
+## Project 4: Password Generator
+
+### 🎯 Concepts Covered:
+* String manipulation
+* Random module
+* Lists
+* User input validation
+
+### 📝 Project Description:
+Generate secure random passwords based on user specifications.
+
+### 💻 Complete Code:
+
+```python
+import random
+import string
+
+def generate_password(length, use_uppercase, use_numbers, use_symbols):
+ """
+ Generate a random password based on specifications
+ """
+ # Start with lowercase letters
+ characters = string.ascii_lowercase
+
+ # Add character types based on user preferences
+ if use_uppercase:
+ characters += string.ascii_uppercase
+ if use_numbers:
+ characters += string.digits
+ if use_symbols:
+ characters += string.punctuation
+
+ # Generate password
+ password = ''.join(random.choice(characters) for _ in range(length))
+ return password
+
+def get_user_preferences():
+ """Get password preferences from user"""
+ print("\n🔐 " + "="*50)
+ print(" PASSWORD GENERATOR")
+ print("="*50)
+
+ # Get password length
+ while True:
+ try:
+ length = int(input("\n📏 Enter password length (minimum 4): "))
+ if length >= 4:
+ break
+ else:
+ print("❌ Length must be at least 4 characters!")
+ except ValueError:
+ print("❌ Please enter a valid number!")
+
+ # Get character type preferences
+ print("\n🎨 Select character types to include:")
+ use_uppercase = input("Include UPPERCASE letters? (y/n): ").lower() == 'y'
+ use_numbers = input("Include NUMBERS? (y/n): ").lower() == 'y'
+ use_symbols = input("Include SYMBOLS? (y/n): ").lower() == 'y'
+
+ return length, use_uppercase, use_numbers, use_symbols
+
+def password_strength(password):
+ """Evaluate password strength"""
+ score = 0
+
+ if len(password) >= 8:
+ score += 1
+ if len(password) >= 12:
+ score += 1
+ if any(c.isupper() for c in password):
+ score += 1
+ if any(c.isdigit() for c in password):
+ score += 1
+ if any(c in string.punctuation for c in password):
+ score += 1
+
+ if score <= 2:
+ return "Weak 😟", "🔴"
+ elif score <= 3:
+ return "Medium 😐", "🟡"
+ else:
+ return "Strong 😊", "🟢"
+
+def password_generator_app():
+ """Main password generator application"""
+ while True:
+ # Get user preferences
+ length, use_upper, use_nums, use_syms = get_user_preferences()
+
+ # Generate password
+ password = generate_password(length, use_upper, use_nums, use_syms)
+
+ # Evaluate strength
+ strength, indicator = password_strength(password)
+
+ # Display result
+ print("\n" + "="*50)
+ print("✅ PASSWORD GENERATED SUCCESSFULLY!")
+ print("="*50)
+ print(f"\n🔑 Your Password: {password}")
+ print(f"\n📊 Password Strength: {indicator} {strength}")
+ print(f"📏 Length: {len(password)} characters")
+ print("="*50)
+
+ # Ask to generate another
+ again = input("\n🔄 Generate another password? (y/n): ").lower()
+ if again != 'y':
+ print("\n👋 Thank you for using Password Generator! Stay secure!")
+ break
+
+# Run the application
+if __name__ == "__main__":
+ password_generator_app()
+```
+
+### 🔍 How It Works:
+
+1. **String Constants**: Uses `string` module for character sets
+2. **Random Selection**: `random.choice()` picks random characters
+3. **List Comprehension**: Generates password efficiently
+4. **Validation**: Ensures minimum password length
+5. **Strength Checker**: Evaluates password based on criteria
+
+### 🚀 Extensions:
+
+* Add option to exclude ambiguous characters (0, O, l, 1)
+* Implement password strength meter with visual bars
+* Save generated passwords (encrypted)
+* Add passphrase generator option
+* Copy password to clipboard automatically
+
+---
+
+## 📝 Quiz 4: Password Generator
+
+**Q1. Which module provides pre-defined character sets like letters and digits?**
+- A) random
+- B) string
+- C) characters
+- D) letters
+
+
+Show Answer
+**Answer: B) string**
+
+The `string` module provides constants like `ascii_letters`, `digits`, and `punctuation`.
+
+
+**Q2. What makes a password strong?**
+- A) Only using lowercase letters
+- B) Being very short
+- C) Using a mix of uppercase, lowercase, numbers, and symbols with good length
+- D) Using dictionary words
+
+
+Show Answer
+**Answer: C) Using a mix of uppercase, lowercase, numbers, and symbols with good length**
+
+Strong passwords are long and use a variety of character types, making them harder to crack.
+
+
+---
+
+## Project 5: Rock, Paper, Scissors Game
+
+### 🎯 Concepts Covered:
+* Conditional logic
+* Random choices
+* Game loops
+* Score tracking
+
+### 📝 Project Description:
+Classic Rock, Paper, Scissors game against the computer with score tracking.
+
+### 💻 Complete Code:
+
+```python
+import random
+
+def get_computer_choice():
+ """Computer randomly selects rock, paper, or scissors"""
+ return random.choice(['rock', 'paper', 'scissors'])
+
+def determine_winner(player, computer):
+ """Determine the winner of the round"""
+ if player == computer:
+ return "tie"
+
+ winning_combinations = {
+ 'rock': 'scissors',
+ 'scissors': 'paper',
+ 'paper': 'rock'
+ }
+
+ if winning_combinations[player] == computer:
+ return "player"
+ else:
+ return "computer"
+
+def display_choice(choice):
+ """Display emoji for choice"""
+ emojis = {
+ 'rock': '🪨',
+ 'paper': '📄',
+ 'scissors': '✂️'
+ }
+ return emojis.get(choice, '')
+
+def rock_paper_scissors():
+ """Main game function"""
+ print("🎮 " + "="*50)
+ print(" ROCK 🪨 PAPER 📄 SCISSORS ✂️")
+ print("="*50)
+ print("\nInstructions: Type 'rock', 'paper', or 'scissors'")
+ print("Type 'quit' to exit the game\n")
+
+ # Score tracking
+ scores = {'player': 0, 'computer': 0, 'ties': 0}
+ rounds = 0
+
+ while True:
+ print("-" * 50)
+ player_choice = input("👤 Your choice: ").lower().strip()
+
+ if player_choice == 'quit':
+ break
+
+ if player_choice not in ['rock', 'paper', 'scissors']:
+ print("❌ Invalid choice! Please choose rock, paper, or scissors.")
+ continue
+
+ # Computer makes choice
+ computer_choice = get_computer_choice()
+ rounds += 1
+
+ # Display choices
+ print(f"\n🎯 Round {rounds}")
+ print(f"👤 You chose: {display_choice(player_choice)} {player_choice}")
+ print(f"🤖 Computer chose: {display_choice(computer_choice)} {computer_choice}")
+
+ # Determine winner
+ result = determine_winner(player_choice, computer_choice)
+
+ if result == "tie":
+ print("\n🤝 It's a TIE!")
+ scores['ties'] += 1
+ elif result == "player":
+ print("\n🎉 YOU WIN this round!")
+ scores['player'] += 1
+ else:
+ print("\n😞 COMPUTER WINS this round!")
+ scores['computer'] += 1
+
+ # Display current score
+ print(f"\n📊 Score - You: {scores['player']} | Computer: {scores['computer']} | Ties: {scores['ties']}")
+
+ # Final results
+ print("\n" + "="*50)
+ print(" GAME OVER - FINAL RESULTS")
+ print("="*50)
+ print(f"Total Rounds: {rounds}")
+ print(f"Your Wins: {scores['player']}")
+ print(f"Computer Wins: {scores['computer']}")
+ print(f"Ties: {scores['ties']}")
+
+ if scores['player'] > scores['computer']:
+ print("\n🏆 CONGRATULATIONS! YOU ARE THE CHAMPION! 🏆")
+ elif scores['computer'] > scores['player']:
+ print("\n🤖 Computer wins the game! Better luck next time!")
+ else:
+ print("\n🤝 It's a tie overall! Well played!")
+
+ print("\n👋 Thanks for playing!")
+
+# Run the game
+if __name__ == "__main__":
+ rock_paper_scissors()
+```
+
+### 🔍 How It Works:
+
+1. **Random Selection**: Computer choice using `random.choice()`
+2. **Winner Logic**: Dictionary maps winning combinations
+3. **Score Tracking**: Dictionary stores player, computer, and tie scores
+4. **Round System**: Loop continues until player quits
+5. **Visual Feedback**: Emojis make the game more engaging
+
+### 🚀 Extensions:
+
+* Add Rock-Paper-Scissors-Lizard-Spock variant
+* Implement best-of-N rounds
+* Add difficulty levels (computer strategies)
+* Create GUI version using tkinter
+* Add sound effects and animations
+
+---
+
+## 📝 Quiz 5: Rock Paper Scissors Game
+
+**Q1. What is the best data structure to store win conditions?**
+- A) List
+- B) Dictionary
+- C) Tuple
+- D) Set
+
+
+Show Answer
+**Answer: B) Dictionary**
+
+A dictionary efficiently maps each choice to what it beats, making the win logic simple and readable.
+
+
+**Q2. How does `random.choice()` work?**
+- A) It removes an item from the list
+- B) It returns a random item from a sequence
+- C) It sorts the list randomly
+- D) It creates a new random list
+
+
+Show Answer
+**Answer: B) It returns a random item from a sequence**
+
+`random.choice()` randomly selects and returns one element from a non-empty sequence.
+
+
+---
+
+## Project 6: Simple Quiz Game
+
+### 🎯 Concepts Covered:
+* Lists and dictionaries
+* Loops
+* Score calculation
+* Data organization
+
+### 📝 Project Description:
+A quiz game that asks multiple-choice questions and tracks the score.
+
+### 💻 Complete Code:
+
+```python
+def load_questions():
+ """Load quiz questions"""
+ questions = [
+ {
+ 'question': 'What is the capital of France?',
+ 'options': ['A) London', 'B) Berlin', 'C) Paris', 'D) Madrid'],
+ 'answer': 'C'
+ },
+ {
+ 'question': 'What is 2 + 2?',
+ 'options': ['A) 3', 'B) 4', 'C) 5', 'D) 6'],
+ 'answer': 'B'
+ },
+ {
+ 'question': 'Which planet is known as the Red Planet?',
+ 'options': ['A) Venus', 'B) Mars', 'C) Jupiter', 'D) Saturn'],
+ 'answer': 'B'
+ },
+ {
+ 'question': 'What is the largest mammal in the world?',
+ 'options': ['A) Elephant', 'B) Blue Whale', 'C) Giraffe', 'D) Polar Bear'],
+ 'answer': 'B'
+ },
+ {
+ 'question': 'In which year did World War II end?',
+ 'options': ['A) 1943', 'B) 1944', 'C) 1945', 'D) 1946'],
+ 'answer': 'C'
+ }
+ ]
+ return questions
+
+def display_question(question_num, question_data):
+ """Display a single question"""
+ print(f"\n{'='*50}")
+ print(f"Question {question_num}")
+ print('='*50)
+ print(f"\n{question_data['question']}\n")
+ for option in question_data['options']:
+ print(f" {option}")
+ print()
+
+def quiz_game():
+ """Main quiz game function"""
+ print("🎓 " + "="*50)
+ print(" WELCOME TO THE QUIZ GAME!")
+ print("="*50)
+ print("\nAnswer each question by typing A, B, C, or D")
+ print("Let's test your knowledge!\n")
+
+ input("Press Enter to start...")
+
+ # Load questions
+ questions = load_questions()
+ score = 0
+ total_questions = len(questions)
+
+ # Ask each question
+ for i, question in enumerate(questions, 1):
+ display_question(i, question)
+
+ # Get user answer
+ while True:
+ user_answer = input("Your answer: ").upper().strip()
+ if user_answer in ['A', 'B', 'C', 'D']:
+ break
+ print("❌ Invalid input! Please enter A, B, C, or D.")
+
+ # Check answer
+ if user_answer == question['answer']:
+ print("✅ Correct! Well done!")
+ score += 1
+ else:
+ print(f"❌ Wrong! The correct answer was {question['answer']}")
+
+ input("\nPress Enter for next question...")
+
+ # Display final results
+ print("\n" + "="*50)
+ print(" QUIZ COMPLETED!")
+ print("="*50)
+ print(f"\n📊 Your Score: {score}/{total_questions}")
+
+ # Calculate percentage
+ percentage = (score / total_questions) * 100
+ print(f"📈 Percentage: {percentage:.1f}%")
+
+ # Grade
+ if percentage >= 90:
+ grade = "A+ 🌟"
+ message = "Outstanding! You're a genius!"
+ elif percentage >= 80:
+ grade = "A 😊"
+ message = "Excellent work!"
+ elif percentage >= 70:
+ grade = "B 👍"
+ message = "Good job!"
+ elif percentage >= 60:
+ grade = "C 😐"
+ message = "Not bad, but you can do better!"
+ else:
+ grade = "F 😞"
+ message = "Keep studying and try again!"
+
+ print(f"🎯 Grade: {grade}")
+ print(f"💬 {message}")
+ print("\n" + "="*50)
+ print("Thanks for playing!")
+
+# Run the quiz
+if __name__ == "__main__":
+ quiz_game()
+```
+
+### 🔍 How It Works:
+
+1. **Data Structure**: List of dictionaries for questions
+2. **Loop Through Questions**: Enumerate for question numbering
+3. **Input Validation**: While loop ensures valid answer format
+4. **Score Tracking**: Increment score for correct answers
+5. **Grading System**: Calculate percentage and assign grade
+
+### 🚀 Extensions:
+
+* Load questions from external file (JSON/CSV)
+* Add multiple quiz categories
+* Implement timer for each question
+* Add lifelines (50:50, skip question)
+* Create leaderboard system
+* Add difficulty levels
+
+---
+
+## 📝 Final Quiz: Comprehensive Project Review
+
+**Q1. What is the main benefit of organizing questions as a list of dictionaries?**
+- A) It makes the code shorter
+- B) It allows easy addition/modification of questions and structured data access
+- C) It makes the code run faster
+- D) It's required by Python
+
+
+Show Answer
+**Answer: B) It allows easy addition/modification of questions and structured data access**
+
+Using a list of dictionaries makes the quiz scalable and maintains a clear structure for each question's data.
+
+
+**Q2. Which project concept is common across most of these projects?**
+- A) File handling
+- B) User input and validation
+- C) Database operations
+- D) Web scraping
+
+
+Show Answer
+**Answer: B) User input and validation**
+
+All projects require user input and proper validation to ensure the program works correctly with various inputs.
+
+
+**Q3. Why is error handling important in these projects?**
+- A) To make the code longer
+- B) To prevent crashes from invalid user input
+- C) It's not important
+- D) To slow down the program
+
+
+Show Answer
+**Answer: B) To prevent crashes from invalid user input**
+
+Error handling (try-except blocks) prevents programs from crashing when users enter unexpected input.
+
+
+**Q4. What programming concept is demonstrated by breaking code into functions?**
+- A) Recursion
+- B) Inheritance
+- C) Modular programming
+- D) Polymorphism
+
+
+Show Answer
+**Answer: C) Modular programming**
+
+Breaking code into functions is modular programming, which improves code organization, reusability, and maintainability.
+
+
+**Q5. Which project would be best to learn about data structures?**
+- A) Number Guessing Game
+- B) Calculator
+- C) To-Do List or Quiz Game
+- D) Rock Paper Scissors
+
+
+Show Answer
+**Answer: C) To-Do List or Quiz Game**
+
+---
+
+🎉 **Congratulations!** You've mastered many Programs and made Small projects. Now make more projects !! Happy Coding
\ No newline at end of file
diff --git a/docs/python/python-string.md b/docs/python/python-string.md
index e693d7ff..1065ade4 100644
--- a/docs/python/python-string.md
+++ b/docs/python/python-string.md
@@ -23,7 +23,7 @@ It is used to store and manipulate **textual data**.
str1 = 'Hello'
str2 = "World"
str3 = '''This is a multi-line string.'''
-````
+```
## Creating Strings
@@ -37,6 +37,112 @@ is a
multiline string."""
```
+---
+
+## 🎯 Quiz 1: String Basics and Creation
+
+**Question 1:** What's the difference between single, double, and triple quotes?
+
+Show Answer
+
+**Single (`'`) and Double (`"`) quotes:**
+- Functionally identical
+- Use one when the other appears in the string
+
+```python
+str1 = 'Hello'
+str2 = "Hello"
+print(str1 == str2) # True
+
+# Use single quotes when string contains double quotes
+sentence = 'He said "Hello"'
+
+# Use double quotes when string contains single quotes
+text = "It's a beautiful day"
+```
+
+**Triple quotes (`'''` or `"""`):**
+- Allow multi-line strings
+- Preserve line breaks and formatting
+- Often used for docstrings
+
+```python
+multiline = """Line 1
+Line 2
+Line 3"""
+
+# Function docstring
+def greet():
+ """
+ This function greets the user.
+ It takes no parameters.
+ """
+ pass
+```
+
+
+**Question 2:** Are strings mutable or immutable in Python?
+
+Show Answer
+
+**Strings are IMMUTABLE** - you cannot change individual characters.
+
+```python
+text = "Hello"
+# text[0] = "J" # TypeError: 'str' object does not support item assignment
+
+# Instead, create a new string
+text = "J" + text[1:]
+print(text) # Jello
+```
+
+**Implications:**
+- String operations create new strings
+- String concatenation in loops is inefficient
+- Use list or join() for building strings
+
+```python
+# Inefficient - creates many intermediate strings
+result = ""
+for i in range(1000):
+ result += str(i) # Creates new string each time
+
+# Efficient - build list then join
+parts = []
+for i in range(1000):
+ parts.append(str(i))
+result = "".join(parts)
+```
+
+
+**Question 3:** What will be the output?
+```python
+text = "Python"
+print(type(text))
+print(len(text))
+```
+
+Show Answer
+
+**Output:**
+```
+
+6
+```
+
+**Explanation:**
+- `type(text)` returns the data type - string class
+- `len(text)` returns the number of characters - 6 letters
+
+**Note:** Spaces and special characters also count:
+```python
+text = "Hello World!"
+print(len(text)) # 12 (includes space and exclamation)
+```
+
+
+---
+
## String Indexing and Slicing
**Indexing**: Access characters by position (starting at index 0).
@@ -55,6 +161,107 @@ print(text[::2]) # Pto
print(text[1:-1]) # ytho
```
+---
+
+## 🎯 Quiz 2: Indexing and Slicing
+
+**Question 1:** Explain positive and negative indexing:
+```python
+text = "Python"
+```
+
+Show Answer
+
+**Positive indexing (left to right, starts at 0):**
+```
+P y t h o n
+0 1 2 3 4 5
+```
+
+**Negative indexing (right to left, starts at -1):**
+```
+P y t h o n
+-6 -5 -4 -3 -2 -1
+```
+
+**Examples:**
+```python
+text = "Python"
+print(text[0]) # P (first character)
+print(text[-1]) # n (last character)
+print(text[2]) # t (third character)
+print(text[-3]) # h (third from end)
+```
+
+**Use case:** Negative indexing is useful for accessing from the end without knowing the length.
+
+
+**Question 2:** What will these slicing operations return?
+```python
+word = "Programming"
+print(word[2:5])
+print(word[:4])
+print(word[6:])
+print(word[::2])
+print(word[::-1])
+```
+
+Show Answer
+
+**Outputs:**
+```python
+print(word[2:5]) # ogr (index 2, 3, 4)
+print(word[:4]) # Prog (start to index 3)
+print(word[6:]) # mming (index 6 to end)
+print(word[::2]) # Pormig (every 2nd character)
+print(word[::-1]) # gnimmargorP (reversed)
+```
+
+**Slicing syntax: `[start:stop:step]`**
+- `start`: starting index (inclusive)
+- `stop`: ending index (exclusive)
+- `step`: increment (default 1)
+
+**Common patterns:**
+```python
+text = "Hello"
+text[:] # Entire string (copy)
+text[1:] # Everything except first
+text[:-1] # Everything except last
+text[::-1] # Reverse the string
+text[::3] # Every 3rd character
+```
+
+
+**Question 3:** What happens when you slice beyond the string length?
+```python
+text = "Hi"
+print(text[0:100])
+print(text[5])
+```
+
+Show Answer
+
+**Slicing is forgiving:**
+```python
+text = "Hi"
+print(text[0:100]) # Hi (returns what exists)
+```
+
+**Indexing raises an error:**
+```python
+print(text[5]) # IndexError: string index out of range
+```
+
+**Explanation:**
+- **Slicing** returns whatever is available (safe)
+- **Indexing** expects exact position (strict)
+
+This is why slicing is often safer for string manipulation.
+
+
+---
+
## String Methods
| Method | Description |
@@ -75,6 +282,103 @@ print(msg.strip()) # Hello Python
print(msg.replace("Python", "JS")) # Hello JS
```
+---
+
+## 🎯 Quiz 3: String Methods
+
+**Question 1:** What's the difference between `strip()`, `lstrip()`, and `rstrip()`?
+
+Show Answer
+
+```python
+text = " Hello World "
+
+print(text.strip()) # "Hello World" (both ends)
+print(text.lstrip()) # "Hello World " (left side only)
+print(text.rstrip()) # " Hello World" (right side only)
+```
+
+**They also remove other characters:**
+```python
+text = "***Hello***"
+print(text.strip("*")) # "Hello"
+
+url = "https://example.com/"
+print(url.rstrip("/")) # "https://example.com"
+```
+
+**Common use case:** Cleaning user input
+```python
+username = input("Enter username: ").strip()
+```
+
+
+**Question 2:** What will be the output?
+```python
+text = "banana"
+print(text.count("a"))
+print(text.count("na"))
+print(text.find("na"))
+print(text.find("x"))
+```
+
+Show Answer
+
+**Outputs:**
+```python
+print(text.count("a")) # 3 (a appears 3 times)
+print(text.count("na")) # 2 (na appears twice)
+print(text.find("na")) # 2 (first occurrence at index 2)
+print(text.find("x")) # -1 (not found)
+```
+
+**Key differences:**
+- `count()` returns the number of occurrences
+- `find()` returns the index of first occurrence (-1 if not found)
+- `index()` is similar to `find()` but raises error if not found
+
+```python
+text = "hello"
+print(text.find("x")) # -1
+# print(text.index("x")) # ValueError: substring not found
+```
+
+
+**Question 3:** Are string methods destructive?
+```python
+text = "Hello"
+result = text.upper()
+```
+
+Show Answer
+
+**No! String methods return NEW strings** (strings are immutable).
+
+```python
+text = "Hello"
+result = text.upper()
+
+print(text) # Hello (unchanged)
+print(result) # HELLO (new string)
+```
+
+**You must reassign to change the variable:**
+```python
+text = "Hello"
+text = text.upper()
+print(text) # HELLO
+```
+
+**Method chaining works because each returns a new string:**
+```python
+text = " hello world "
+result = text.strip().upper().replace("WORLD", "PYTHON")
+print(result) # HELLO PYTHON
+```
+
+
+---
+
## String Concatenation and Repetition
**Concatenation** with `+`:
@@ -101,6 +405,131 @@ print("fun" in text) # True
print("Java" not in text) # True
```
+---
+
+## 🎯 Quiz 4: String Operations
+
+**Question 1:** What are the performance implications of string concatenation in loops?
+
+Show Answer
+
+**Problem: String concatenation in loops is SLOW** (creates new string each time)
+
+```python
+# Inefficient - O(n²) time complexity
+result = ""
+for i in range(10000):
+ result += str(i) # Creates new string each iteration
+```
+
+**Better approaches:**
+
+**Method 1: List + join() (Best)**
+```python
+parts = []
+for i in range(10000):
+ parts.append(str(i))
+result = "".join(parts) # O(n) time complexity
+```
+
+**Method 2: List comprehension + join()**
+```python
+result = "".join(str(i) for i in range(10000))
+```
+
+**Method 3: f-string for small concatenations**
+```python
+name = "Alice"
+age = 25
+result = f"{name} is {age} years old"
+```
+
+**Rule of thumb:** Use `join()` for multiple concatenations, `+` or f-strings for 2-3 strings.
+
+
+**Question 2:** What will happen?
+```python
+print("Python" * 3)
+print(3 * "Python")
+print("Python" + 3)
+```
+
+Show Answer
+
+**Outputs:**
+```python
+print("Python" * 3) # PythonPythonPython
+print(3 * "Python") # PythonPythonPython (commutative)
+print("Python" + 3) # TypeError: can only concatenate str
+```
+
+**Explanation:**
+- `*` works with string and integer (in any order)
+- `+` only works with two strings
+
+**Fix for the error:**
+```python
+print("Python" + str(3)) # Python3
+print(f"Python{3}") # Python3
+```
+
+**Practical uses:**
+```python
+# Create separator lines
+print("=" * 50)
+
+# Padding
+print("Title".center(20, "-")) # -------Title--------
+
+# Repeat patterns
+print("Ha" * 5) # HaHaHaHaHa
+```
+
+
+**Question 3:** How do `in` and substring methods differ?
+
+Show Answer
+
+**`in` operator - Returns boolean (True/False)**
+```python
+text = "Python is awesome"
+print("awesome" in text) # True
+print("java" in text) # False
+```
+
+**`find()` method - Returns index position or -1**
+```python
+print(text.find("awesome")) # 10 (index)
+print(text.find("java")) # -1 (not found)
+```
+
+**`count()` method - Returns number of occurrences**
+```python
+text = "banana"
+print(text.count("a")) # 3
+```
+
+**When to use each:**
+- Use `in` when you only need yes/no
+- Use `find()` when you need the position
+- Use `count()` when you need frequency
+
+**Example:**
+```python
+email = "user@example.com"
+
+# Quick check
+if "@" in email and "." in email:
+ print("Valid format")
+
+# Get position for parsing
+at_position = email.find("@")
+username = email[:at_position]
+```
+
+
+---
+
## String Formatting
### f-string (Python 3.6+)
@@ -125,6 +554,151 @@ print("My name is %s and I am %d years old." % (name, age))
---
+## 🎯 Quiz 5: String Formatting
+
+**Question 1:** What are the advantages of f-strings over other formatting methods?
+
+Show Answer
+
+**f-strings are:**
+1. **More readable** - variables directly in string
+2. **Faster** - evaluated at runtime
+3. **More concise** - less syntax
+4. **Support expressions** - can include calculations
+
+**Comparison:**
+```python
+name = "Alice"
+age = 25
+
+# f-string (best)
+print(f"Name: {name}, Age: {age}")
+
+# format()
+print("Name: {}, Age: {}".format(name, age))
+
+# % operator (old style)
+print("Name: %s, Age: %d" % (name, age))
+```
+
+**f-string advantages:**
+```python
+# Expressions
+print(f"Next year: {age + 1}")
+
+# Formatting
+pi = 3.14159
+print(f"Pi: {pi:.2f}") # Pi: 3.14
+
+# Debugging (Python 3.8+)
+x = 42
+print(f"{x=}") # x=42
+
+# Alignment
+print(f"{'Left':<10}{'Right':>10}{'Center':^10}")
+```
+
+
+**Question 2:** Format numbers and dates with f-strings:
+
+Show Answer
+
+**Number formatting:**
+```python
+# Decimal places
+pi = 3.14159
+print(f"{pi:.2f}") # 3.14
+print(f"{pi:.4f}") # 3.1416
+
+# Thousands separator
+large_num = 1000000
+print(f"{large_num:,}") # 1,000,000
+
+# Percentage
+ratio = 0.75
+print(f"{ratio:.1%}") # 75.0%
+
+# Scientific notation
+big = 12345678
+print(f"{big:.2e}") # 1.23e+07
+
+# Padding with zeros
+num = 42
+print(f"{num:05d}") # 00042
+```
+
+**Date formatting:**
+```python
+from datetime import datetime
+
+now = datetime.now()
+print(f"{now:%Y-%m-%d}") # 2024-03-15
+print(f"{now:%B %d, %Y}") # March 15, 2024
+print(f"{now:%I:%M %p}") # 03:30 PM
+```
+
+**Alignment and width:**
+```python
+items = ["Apple", "Banana", "Cherry"]
+prices = [1.50, 0.75, 2.00]
+
+for item, price in zip(items, prices):
+ print(f"{item:<10} ${price:>6.2f}")
+# Apple $ 1.50
+# Banana $ 0.75
+# Cherry $ 2.00
+```
+
+
+**Question 3:** When should you NOT use f-strings?
+
+Show Answer
+
+**Avoid f-strings when:**
+
+**1. Template strings reused multiple times:**
+```python
+# Don't do this
+def greet_user(name):
+ return f"Hello, {name}!"
+
+# Better - reusable template
+template = "Hello, {}!"
+print(template.format("Alice"))
+print(template.format("Bob"))
+```
+
+**2. User-provided format strings (security risk):**
+```python
+# Dangerous - allows code execution
+user_input = input("Format: ")
+# eval(f"f'{user_input}'") # NEVER DO THIS
+
+# Safe alternative
+template = "Value: {}"
+print(template.format(user_input))
+```
+
+**3. Logging with lazy evaluation:**
+```python
+import logging
+
+# f-string evaluates immediately (inefficient)
+logging.debug(f"Complex calculation: {expensive_function()}")
+
+# Better - only evaluates if needed
+logging.debug("Complex calculation: %s", expensive_function())
+```
+
+**4. Python version < 3.6:**
+```python
+# Use format() for compatibility
+"Name: {}, Age: {}".format(name, age)
+```
+
+
+---
+
## Escape Sequences
Escape characters add special formatting in strings:
@@ -154,6 +728,86 @@ This is line 3"""
print(message)
```
+---
+
+## 🎯 Quiz 6: Escape Sequences and Special Strings
+
+**Question 1:** How do you include quotes within strings?
+
+Show Answer
+
+**Method 1: Escape characters**
+```python
+text1 = "He said \"Hello\""
+text2 = 'It\'s a beautiful day'
+print(text1) # He said "Hello"
+print(text2) # It's a beautiful day
+```
+
+**Method 2: Use opposite quote type**
+```python
+text1 = 'He said "Hello"'
+text2 = "It's a beautiful day"
+```
+
+**Method 3: Triple quotes**
+```python
+text = '''He said "Hello" and I replied "It's nice!"'''
+```
+
+**Common mistake:**
+```python
+# Error - unescaped quote
+# text = "He said "Hello"" # SyntaxError
+
+# Fixed
+text = "He said \"Hello\""
+```
+
+
+**Question 2:** What's the difference between raw strings and regular strings?
+
+Show Answer
+
+**Regular strings process escape sequences:**
+```python
+path = "C:\new\test"
+print(path) # C:
+ # ew est (Unexpected!)
+```
+
+**Raw strings (prefix with `r`) treat backslashes literally:**
+```python
+path = r"C:\new\test"
+print(path) # C:\new\test (Correct!)
+```
+
+**Use cases:**
+
+**1. File paths (Windows)**
+```python
+file_path = r"C:\Users\Documents\file.txt"
+```
+
+**2. Regular expressions**
+```python
+import re
+pattern = r"\d{3}-\d{3}-\d{4}" # Phone pattern
+```
+
+**3. LaTeX formulas**
+```python
+formula = r"\frac{a}{b} = c"
+```
+
+**Note:** Raw strings still can't end with a single backslash:
+```python
+# path = r"C:\folder\" # SyntaxError
+path = r"C:\folder" + "\\" # Workaround
+```
+
+
+---
## Use Cases and Examples
@@ -187,6 +841,214 @@ if email.endswith("@example.com"):
print("Valid domain")
```
+---
+
+## 🎯 Final Quiz: Comprehensive String Challenge
+
+**Question 1:** Write a function to check if a string is a palindrome (case-insensitive):
+
+Show Answer
+
+```python
+def is_palindrome(text):
+ """Check if string is palindrome (case-insensitive)."""
+ # Remove spaces and convert to lowercase
+ cleaned = text.replace(" ", "").lower()
+ return cleaned == cleaned[::-1]
+
+# Tests
+print(is_palindrome("radar")) # True
+print(is_palindrome("Racecar")) # True
+print(is_palindrome("A man a plan a canal Panama")) # True
+print(is_palindrome("hello")) # False
+
+# Alternative - more robust (remove all non-alphanumeric)
+import re
+
+def is_palindrome_robust(text):
+ cleaned = re.sub(r'[^a-zA-Z0-9]', '', text).lower()
+ return cleaned == cleaned[::-1]
+
+print(is_palindrome_robust("A man, a plan, a canal: Panama")) # True
+```
+
+
+**Question 2:** Parse and validate an email address:
+
+Show Answer
+
+```python
+def parse_email(email):
+ """Parse and validate email address."""
+ # Basic validation
+ if "@" not in email or email.count("@") != 1:
+ return None
+
+ # Split into parts
+ username, domain = email.split("@")
+
+ # Validate parts
+ if not username or not domain:
+ return None
+
+ if "." not in domain:
+ return None
+
+ # Extract domain parts
+ domain_parts = domain.split(".")
+ domain_name = domain_parts[0]
+ extension = ".".join(domain_parts[1:])
+
+ return {
+ "username": username,
+ "domain": domain_name,
+ "extension": extension,
+ "full": email
+ }
+
+# Tests
+print(parse_email("user@example.com"))
+# {'username': 'user', 'domain': 'example', 'extension': 'com', 'full': 'user@example.com'}
+
+print(parse_email("invalid.email")) # None
+print(parse_email("no@domain")) # None
+```
+
+
+**Question 3:** Implement a simple text censorship function:
+
+Show Answer
+
+```python
+def censor_text(text, bad_words):
+ """Replace bad words with asterisks."""
+ result = text
+ for word in bad_words:
+ # Case-insensitive replacement
+ replacement = "*" * len(word)
+ # Use regex for whole words only
+ import re
+ pattern = re.compile(re.escape(word), re.IGNORECASE)
+ result = pattern.sub(replacement, result)
+ return result
+
+# Test
+text = "This is a bad word and another BAD one"
+bad_words = ["bad"]
+print(censor_text(text, bad_words))
+# This is a *** word and another *** one
+
+# Alternative without regex - simpler but less precise
+def censor_simple(text, bad_words):
+ words = text.split()
+ result = []
+ for word in words:
+ if word.lower() in [w.lower() for w in bad_words]:
+ result.append("*" * len(word))
+ else:
+ result.append(word)
+ return " ".join(result)
+```
+
+
+**Question 4:** Create a function to title-case a string properly:
+
+Show Answer
+
+```python
+def smart_title_case(text):
+ """
+ Title case with proper handling of small words.
+ Small words like 'a', 'an', 'the', 'and', 'or' stay lowercase.
+ """
+ small_words = {'a', 'an', 'the', 'and', 'or', 'but', 'for', 'at', 'by', 'to'}
+
+ words = text.lower().split()
+ result = []
+
+ for i, word in enumerate(words):
+ # Always capitalize first and last word
+ if i == 0 or i == len(words) - 1:
+ result.append(word.capitalize())
+ # Keep small words lowercase
+ elif word in small_words:
+ result.append(word)
+ # Capitalize other words
+ else:
+ result.append(word.capitalize())
+
+ return " ".join(result)
+
+# Tests
+print(smart_title_case("the lord of the rings"))
+# The Lord of the Rings
+
+print(smart_title_case("a tale of two cities"))
+# A Tale of Two Cities
+
+# Compare with built-in
+text = "the lord of the rings"
+print(text.title()) # The Lord Of The Rings (wrong)
+print(smart_title_case(text)) # The Lord of the Rings (correct)
+```
+
+
+**Question 5:** Build a simple template engine:
+
+Show Answer
+
+```python
+def simple_template(template, **kwargs):
+ """
+ Simple template engine using {{variable}} syntax.
+ """
+ result = template
+ for key, value in kwargs.items():
+ placeholder = "{{" + key + "}}"
+ result = result.replace(placeholder, str(value))
+ return result
+
+# Usage
+email_template = """
+Hello {{name}},
+
+Your order #{{order_id}} has been shipped!
+Total: ${{total}}
+
+Thank you for shopping with us.
+"""
+
+message = simple_template(
+ email_template,
+ name="Alice",
+ order_id=12345,
+ total=99.99
+)
+print(message)
+
+# More advanced with format()
+def template_format(template, **kwargs):
+ """Using Python's built-in format for more features."""
+ return template.format(**kwargs)
+
+invoice = """
+Invoice #{invoice_num}
+Customer: {customer}
+Amount: ${amount:.2f}
+Date: {date}
+"""
+
+print(template_format(
+ invoice,
+ invoice_num=101,
+ customer="Bob Smith",
+ amount=150.5,
+ date="2024-03-15"
+))
+```
+
+
+---
## Summary
@@ -195,3 +1057,7 @@ if email.endswith("@example.com"):
* Useful **methods** help in text processing.
* Use **escape sequences** for formatting.
* Use **f-strings** or `format()` for clean formatting.
+
+---
+
+🎉 **Congratulations!** You've mastered Strings in Python. Practice with text processing, parsing, and formatting to strengthen your string manipulation skills!
\ No newline at end of file
diff --git a/docs/python/python-syntax.md b/docs/python/python-syntax.md
index 62ed1eb0..9cc948a6 100644
--- a/docs/python/python-syntax.md
+++ b/docs/python/python-syntax.md
@@ -26,7 +26,7 @@ Python uses indentation instead of curly braces `{}` to define blocks of code.
```python
if 5 > 2:
print("Five is greater than two!")
-````
+```
* **Indentation** is crucial in Python. Missing or incorrect indentation will raise an error.
@@ -52,6 +52,36 @@ print("Hello again!")
---
+## 📝 Quiz 1: Basics and Syntax
+
+**Q1. What does Python use to define blocks of code?**
+- A) Curly braces `{}`
+- B) Parentheses `()`
+- C) Indentation
+- D) Semicolons `;`
+
+
+Show Answer
+**Answer: C) Indentation**
+
+Python uses indentation (typically 4 spaces) to define code blocks, unlike many other languages that use curly braces.
+
+
+**Q2. Which of the following is a valid way to create a multi-line comment in Python?**
+- A) `// comment //`
+- B) `/* comment */`
+- C) `""" comment """`
+- D) ``
+
+
+Show Answer
+**Answer: C) `""" comment """`**
+
+Triple quotes (single or double) are used for multi-line comments in Python.
+
+
+---
+
### Variables
Python does not require you to declare the type of a variable.
@@ -92,6 +122,36 @@ person = {"name": "Bob", "age": 25} # dict
---
+## 📝 Quiz 2: Variables and Data Types
+
+**Q1. What will be the data type of `x` after this assignment: `x = 3.14`?**
+- A) int
+- B) float
+- C) str
+- D) bool
+
+
+Show Answer
+**Answer: B) float**
+
+Numbers with decimal points are automatically assigned the `float` data type in Python.
+
+
+**Q2. Which data type stores unique unordered collections?**
+- A) list
+- B) tuple
+- C) dict
+- D) set
+
+
+Show Answer
+**Answer: D) set**
+
+Sets store unique elements in an unordered collection, automatically removing duplicates.
+
+
+---
+
### Conditionals
```python
@@ -127,6 +187,49 @@ while count < 5:
---
+## 📝 Quiz 3: Conditionals and Loops
+
+**Q1. What will be the output of this code?**
+```python
+age = 15
+if age >= 18:
+ print("Adult")
+elif age > 12:
+ print("Teenager")
+else:
+ print("Child")
+```
+- A) Adult
+- B) Teenager
+- C) Child
+- D) Error
+
+
+Show Answer
+**Answer: B) Teenager**
+
+Since age is 15, it doesn't satisfy the first condition (>= 18) but satisfies the second condition (> 12).
+
+
+**Q2. How many times will this loop execute?**
+```python
+for i in range(5):
+ print(i)
+```
+- A) 4 times
+- B) 5 times
+- C) 6 times
+- D) Infinite times
+
+
+Show Answer
+**Answer: B) 5 times**
+
+`range(5)` generates numbers from 0 to 4, resulting in 5 iterations.
+
+
+---
+
### Functions
Functions are defined using the `def` keyword.
@@ -150,6 +253,42 @@ print(result) # Output: 5
---
+## 📝 Quiz 4: Functions
+
+**Q1. What keyword is used to define a function in Python?**
+- A) function
+- B) def
+- C) func
+- D) define
+
+
+Show Answer
+**Answer: B) def**
+
+The `def` keyword is used to define functions in Python.
+
+
+**Q2. What will be the value of `result` after executing this code?**
+```python
+def multiply(x, y):
+ return x * y
+
+result = multiply(3, 4)
+```
+- A) 7
+- B) 12
+- C) 34
+- D) None
+
+
+Show Answer
+**Answer: B) 12**
+
+The function multiplies 3 and 4, returning 12.
+
+
+---
+
### Modules and Imports
You can import built-in or custom modules.
@@ -184,6 +323,36 @@ and or not
---
+## 📝 Quiz 5: Operators
+
+**Q1. What is the result of `10 // 3` in Python?**
+- A) 3.33
+- B) 3
+- C) 4
+- D) 3.0
+
+
+Show Answer
+**Answer: B) 3**
+
+The `//` operator performs floor division, returning only the integer part of the division.
+
+
+**Q2. Which operator is used for exponentiation in Python?**
+- A) ^
+- B) **
+- C) ^^
+- D) pow
+
+
+Show Answer
+**Answer: B) `**`**
+
+The `**` operator is used for exponentiation (e.g., `2**3` equals 8).
+
+
+---
+
### Indentation Rules
Python uses **4 spaces** (by convention) for indentation. Do not mix tabs and spaces.
@@ -210,4 +379,47 @@ Python syntax is simple, readable, and beginner-friendly. With its use of indent
---
-> 📌 **Note**: Make sure your Python files have the `.py` extension and you're using Python 3.x version for compatibility with modern features.
+## 📝 Final Quiz: Comprehensive Review
+
+**Q1. What will happen if you mix tabs and spaces for indentation in Python?**
+- A) Nothing, it works fine
+- B) Python will automatically convert them
+- C) It may cause IndentationError or inconsistent behavior
+- D) Python will use the first type it encounters
+
+
+Show Answer
+**Answer: C) It may cause IndentationError or inconsistent behavior**
+
+Python requires consistent indentation. Mixing tabs and spaces can lead to errors and is considered bad practice.
+
+
+**Q2. Which of the following statements is TRUE about Python?**
+- A) Python requires semicolons at the end of each statement
+- B) Python is case-sensitive
+- C) Python requires variable type declarations
+- D) Python uses curly braces for code blocks
+
+
+Show Answer
+**Answer: B) Python is case-sensitive**
+
+Python is case-sensitive (e.g., `variable` and `Variable` are different), doesn't require semicolons or type declarations, and uses indentation instead of curly braces.
+
+
+**Q3. What is the correct file extension for Python files?**
+- A) .python
+- B) .pt
+- C) .py
+- D) .pyt
+
+
+Show Answer
+**Answer: C) .py**
+
+Python files use the `.py` extension.
+
+
+---
+
+> 📌 **Note**: Make sure your Python files have the `.py` extension and you're using Python 3.x version for compatibility with modern features.
\ No newline at end of file
diff --git a/docs/python/python-tuple.md b/docs/python/python-tuple.md
index 05da877b..f6d6db39 100644
--- a/docs/python/python-tuple.md
+++ b/docs/python/python-tuple.md
@@ -41,7 +41,7 @@ numbers = 1, 2, 3
# Single-element Tuple (Note the comma!)
single = ("hello",)
-````
+```
**Important:** Without the comma, Python does not recognize it as a tuple:
@@ -49,6 +49,37 @@ single = ("hello",)
not_a_tuple = ("hello") # This is a string, NOT a tuple
```
+---
+
+## 📝 Quiz 1: Creating Tuples
+
+**Q1. Which of the following correctly creates a single-element tuple?**
+- A) `single = ("hello")`
+- B) `single = ("hello",)`
+- C) `single = ["hello"]`
+- D) `single = {"hello"}`
+
+
+Show Answer
+**Answer: B) `single = ("hello",)`**
+
+A single-element tuple requires a trailing comma. Without it, Python treats it as a string in parentheses.
+
+
+**Q2. What is the data type of `x` after this assignment: `x = (1, 2, 3)`?**
+- A) list
+- B) set
+- C) tuple
+- D) dict
+
+
+Show Answer
+**Answer: C) tuple**
+
+Parentheses with comma-separated values create a tuple.
+
+
+---
## Accessing Elements
@@ -75,6 +106,37 @@ print(numbers[:3]) # (10, 20, 30)
print(numbers[2:]) # (30, 40, 50)
```
+---
+
+## 📝 Quiz 2: Accessing and Slicing
+
+**Q1. Given `colors = ("red", "green", "blue", "yellow")`, what will `colors[-2]` return?**
+- A) "red"
+- B) "green"
+- C) "blue"
+- D) "yellow"
+
+
+Show Answer
+**Answer: C) "blue"**
+
+Negative indexing starts from the end: -1 is "yellow", -2 is "blue".
+
+
+**Q2. What will be the output of `numbers[1:4]` for `numbers = (10, 20, 30, 40, 50)`?**
+- A) (10, 20, 30)
+- B) (20, 30, 40)
+- C) (20, 30, 40, 50)
+- D) (30, 40)
+
+
+Show Answer
+**Answer: B) (20, 30, 40)**
+
+Slicing `[1:4]` includes indices 1, 2, and 3 (elements 20, 30, 40), but excludes index 4.
+
+
+---
## Tuple Immutability
@@ -95,6 +157,37 @@ TypeError: 'tuple' object does not support item assignment
This property makes tuples **safe for constant data**, like coordinates, fixed configurations, etc.
+---
+
+## 📝 Quiz 3: Tuple Immutability
+
+**Q1. What happens when you try to modify a tuple element?**
+- A) The element is successfully modified
+- B) Python creates a new tuple
+- C) A TypeError is raised
+- D) The entire tuple is deleted
+
+
+Show Answer
+**Answer: C) A TypeError is raised**
+
+Tuples are immutable, so attempting to modify them raises a `TypeError: 'tuple' object does not support item assignment`.
+
+
+**Q2. Which statement about tuple immutability is TRUE?**
+- A) You can add elements to a tuple using append()
+- B) You can change elements but not delete them
+- C) Tuples cannot be modified after creation
+- D) Only the first element of a tuple is immutable
+
+
+Show Answer
+**Answer: C) Tuples cannot be modified after creation**
+
+Tuples are completely immutable - you cannot add, remove, or modify any elements after creation.
+
+
+---
## Tuple Methods
@@ -114,6 +207,37 @@ print(numbers.count(2)) # 3
print(numbers.index(3)) # 2
```
+---
+
+## 📝 Quiz 4: Tuple Methods
+
+**Q1. Given `nums = (5, 10, 15, 10, 20, 10)`, what will `nums.count(10)` return?**
+- A) 1
+- B) 2
+- C) 3
+- D) 10
+
+
+Show Answer
+**Answer: C) 3**
+
+The `count()` method returns how many times the value 10 appears in the tuple, which is 3 times.
+
+
+**Q2. How many built-in methods do tuples have?**
+- A) 0
+- B) 2
+- C) 5
+- D) Same as lists
+
+
+Show Answer
+**Answer: B) 2**
+
+Tuples have only two built-in methods: `count()` and `index()`.
+
+
+---
## Tuple Packing and Unpacking
@@ -150,6 +274,37 @@ print(nested[1]) # ('a', 'b', 'c')
print(nested[1][2]) # 'c'
```
+---
+
+## 📝 Quiz 5: Packing and Unpacking
+
+**Q1. What is tuple unpacking?**
+- A) Removing elements from a tuple
+- B) Assigning tuple elements to individual variables
+- C) Converting a tuple to a list
+- D) Creating a nested tuple
+
+
+Show Answer
+**Answer: B) Assigning tuple elements to individual variables**
+
+Tuple unpacking assigns each element of a tuple to separate variables in a single statement.
+
+
+**Q2. Given `person = ("Alice", 30, "Developer")` and `name, age, job = person`, what is the value of `age`?**
+- A) "Alice"
+- B) 30
+- C) "Developer"
+- D) Error
+
+
+Show Answer
+**Answer: B) 30**
+
+During unpacking, the second element (30) is assigned to the second variable (age).
+
+
+---
## Tuple vs. List
@@ -194,6 +349,64 @@ squares = tuple(x*x for x in range(5))
print(squares) # (0, 1, 4, 9, 16)
```
+---
+
+## 📝 Final Quiz: Comprehensive Review
+
+**Q1. Which of the following is a valid use case for tuples?**
+- A) Storing a shopping cart that frequently changes
+- B) Storing geographic coordinates that should remain constant
+- C) Implementing a stack with push and pop operations
+- D) Storing a to-do list that gets updated regularly
+
+
+Show Answer
+**Answer: B) Storing geographic coordinates that should remain constant**
+
+Tuples are ideal for fixed data that shouldn't change, like coordinates. For dynamic data that changes frequently, lists are more appropriate.
+
+
+**Q2. What is the main advantage of tuples over lists?**
+- A) Tuples can store more elements
+- B) Tuples are immutable and protect data integrity
+- C) Tuples have more built-in methods
+- D) Tuples use less syntax
+
+
+Show Answer
+**Answer: B) Tuples are immutable and protect data integrity**
+
+The immutability of tuples ensures data cannot be accidentally modified, making them safer for constant data.
+
+
+**Q3. Can tuples be used as dictionary keys?**
+- A) No, never
+- B) Yes, because they are hashable due to immutability
+- C) Only if they contain strings
+- D) Yes, but only empty tuples
+
+
+Show Answer
+**Answer: B) Yes, because they are hashable due to immutability**
+
+Tuples are hashable (because they're immutable), which allows them to be used as dictionary keys, unlike lists.
+
+
+**Q4. Does Python support tuple comprehension?**
+- A) Yes, using parentheses like (x for x in range(5))
+- B) No, but you can use generator expressions and convert to tuple
+- C) Yes, using square brackets
+- D) Yes, using curly braces
+
+
+Show Answer
+**Answer: B) No, but you can use generator expressions and convert to tuple**
+
+Python doesn't have tuple comprehension. Parentheses create generator expressions, which can be converted to tuples using `tuple()`.
+
+
+---
+
## Conclusion
Tuples are a **fundamental** data type in Python, providing a simple, efficient, and immutable way to store ordered data. Understanding when to choose a tuple over a list is essential for writing clear and robust code.
\ No newline at end of file
diff --git a/docs/python/python-variables.md b/docs/python/python-variables.md
index 76dcb1a5..a507a21c 100644
--- a/docs/python/python-variables.md
+++ b/docs/python/python-variables.md
@@ -23,12 +23,12 @@ Variables act as placeholders for data. They allow us to store and reuse values
### 1. What is a Variable?
-A variable is like a container for storing data values. You don’t need to declare its type explicitly — Python handles it dynamically.
+A variable is like a container for storing data values. You don't need to declare its type explicitly — Python handles it dynamically.
```python
x = 5
y = "Hello"
-````
+```
Here:
@@ -65,6 +65,37 @@ You can also assign the **same value to multiple variables**:
a = b = c = 100
```
+---
+
+## 📝 Quiz 1: Variable Basics
+
+**Q1. What happens when you assign a value to a variable in Python?**
+- A) You must declare the variable type first
+- B) Python automatically infers the variable type
+- C) The variable must be initialized with a default value
+- D) You need to use a special keyword like `var`
+
+
+Show Answer
+**Answer: B) Python automatically infers the variable type**
+
+Python uses dynamic typing, meaning the type is determined automatically based on the value assigned.
+
+
+**Q2. What will be the result of this assignment: `a = b = c = 50`?**
+- A) Only `a` gets the value 50
+- B) Error: multiple assignment not allowed
+- C) All three variables (`a`, `b`, `c`) get the value 50
+- D) Only the last variable `c` gets the value 50
+
+
+Show Answer
+**Answer: C) All three variables (`a`, `b`, `c`) get the value 50**
+
+Python allows chained assignment, where the same value is assigned to multiple variables.
+
+
+---
### 4. Variable Naming Rules
@@ -85,6 +116,37 @@ my-var = 20 # hyphen not allowed
def = 30 # 'def' is a keyword
```
+---
+
+## 📝 Quiz 2: Naming Rules
+
+**Q1. Which of the following is a valid variable name in Python?**
+- A) `2fast`
+- B) `my-variable`
+- C) `_private_var`
+- D) `class`
+
+
+Show Answer
+**Answer: C) `_private_var`**
+
+Variable names can start with an underscore. Option A starts with a digit, B contains a hyphen (not allowed), and D is a reserved keyword.
+
+
+**Q2. Are `name` and `Name` the same variable in Python?**
+- A) Yes, Python is case-insensitive
+- B) No, Python is case-sensitive
+- C) They are the same only for strings
+- D) It depends on the Python version
+
+
+Show Answer
+**Answer: B) No, Python is case-sensitive**
+
+Variable names in Python are case-sensitive, so `name` and `Name` are treated as different variables.
+
+
+---
### 5. Standard Data Types in Python
@@ -114,6 +176,37 @@ x = "Hello"
print(type(x)) #
```
+---
+
+## 📝 Quiz 3: Data Types and Dynamic Typing
+
+**Q1. What will be the output of `type(x)` after executing `x = 5` and then `x = "Hello"`?**
+- A) ``
+- B) ``
+- C) Error: cannot change variable type
+- D) ``
+
+
+Show Answer
+**Answer: B) ``**
+
+Python allows dynamic typing, so the variable type changes to string after the second assignment.
+
+
+**Q2. Which function is used to check the data type of a variable?**
+- A) `typeof()`
+- B) `datatype()`
+- C) `type()`
+- D) `check_type()`
+
+
+Show Answer
+**Answer: C) `type()`**
+
+The built-in `type()` function returns the data type of a variable in Python.
+
+
+---
### 8. Deleting a Variable with `del`
@@ -157,7 +250,37 @@ greet()
print(msg) # Error: NameError
```
+---
+
+## 📝 Quiz 4: Variable Scope
+
+**Q1. What happens when you try to access a local variable outside its function?**
+- A) It returns `None`
+- B) It raises a NameError
+- C) It returns the last assigned value
+- D) It creates a new global variable
+
+
+Show Answer
+**Answer: B) It raises a NameError**
+
+Local variables are only accessible within the function where they are defined. Accessing them outside raises a NameError.
+
+
+**Q2. Where is a global variable accessible?**
+- A) Only inside the function where it's used
+- B) Only in the same file
+- C) Anywhere in the program
+- D) Only before function definitions
+
+Show Answer
+**Answer: C) Anywhere in the program**
+
+Global variables are declared outside functions and can be accessed from anywhere in the program.
+
+
+---
### 🟢 The `global` Keyword
@@ -174,6 +297,45 @@ update()
print(x) # Output: 20
```
+---
+
+## 📝 Quiz 5: The global Keyword
+
+**Q1. What is the purpose of the `global` keyword?**
+- A) To create a new global variable
+- B) To modify a global variable inside a function
+- C) To delete a global variable
+- D) To check if a variable is global
+
+
+Show Answer
+**Answer: B) To modify a global variable inside a function**
+
+The `global` keyword allows you to modify global variables from within a function scope.
+
+
+**Q2. What will be the output after running this code?**
+```python
+x = 5
+def change():
+ global x
+ x = 10
+change()
+print(x)
+```
+- A) 5
+- B) 10
+- C) Error
+- D) None
+
+
+Show Answer
+**Answer: B) 10**
+
+The `global` keyword allows the function to modify the global variable `x`, changing it from 5 to 10.
+
+
+---
### 12. Memory Management in Python
@@ -222,6 +384,67 @@ Valid: True
Items: ['pen', 'notebook']
```
+---
+
+## 📝 Final Quiz: Comprehensive Review
+
+**Q1. What does the `del` keyword do?**
+- A) Deletes the value but keeps the variable
+- B) Removes the variable from memory
+- C) Sets the variable to None
+- D) Deletes only the variable type
+
+
+Show Answer
+**Answer: B) Removes the variable from memory**
+
+The `del` keyword completely removes the variable from memory, making it inaccessible.
+
+
+**Q2. What does the `id()` function return?**
+- A) The variable name
+- B) The variable type
+- C) The memory address (reference ID) of the object
+- D) The value of the variable
+
+
+Show Answer
+**Answer: C) The memory address (reference ID) of the object**
+
+The `id()` function returns the unique identifier (memory address) of an object in Python.
+
+
+**Q3. Will `a` and `b` have the same memory address after this code?**
+```python
+a = 100
+b = 100
+```
+- A) No, they always have different addresses
+- B) Yes, because they have the same immutable value
+- C) Only if they are strings
+- D) It depends on the Python version
+
+
+Show Answer
+**Answer: B) Yes, because they have the same immutable value**
+
+Python optimizes memory by reusing the same object for immutable values like small integers, so `a` and `b` may share the same memory address.
+
+
+**Q4. Which statement about Python variables is FALSE?**
+- A) Variables can change type dynamically
+- B) Variable names are case-sensitive
+- C) You must declare variable types before use
+- D) Multiple variables can be assigned in one line
+
+
+Show Answer
+**Answer: C) You must declare variable types before use**
+
+Python uses dynamic typing, so you don't need to declare variable types - they are inferred automatically.
+
+
+---
## Summary
@@ -237,4 +460,4 @@ Items: ['pen', 'notebook']
* Covers both global and local variables
* Explains `del`, `global`, and `id()` functions
* Includes formatted tables and output blocks
-* Beginner-friendly explanation with examples
+* Beginner-friendly explanation with examples
\ No newline at end of file
diff --git a/docs/python/python_oops.md b/docs/python/python_oops.md
index 71cf560a..88a45cd4 100644
--- a/docs/python/python_oops.md
+++ b/docs/python/python_oops.md
@@ -47,6 +47,49 @@ my_car = Car("Tesla", "Model S")
my_car.start() # Tesla Model S is starting...
```
+---
+
+### 🧠 Quiz 1: Classes and Objects
+
+**Question 1:** What is the relationship between a class and an object?
+
+
+Click to reveal answer
+
+**Answer:** A class is a blueprint, and an object is an instance of that class
+
+**Explanation:** Think of a class as a blueprint for a house (defining what it should have), and objects as the actual houses built from that blueprint. Each object can have different values for its attributes.
+
+
+**Question 2:** What will be the output of this code?
+```python
+class Dog:
+ def __init__(self, name):
+ self.name = name
+
+dog1 = Dog("Buddy")
+print(dog1.name)
+```
+
+
+Click to reveal answer
+
+**Answer:** `Buddy`
+
+**Explanation:** The `__init__` method is the constructor that initializes the object. When we create `dog1 = Dog("Buddy")`, the name attribute is set to "Buddy", which is then printed.
+
+
+**Question 3:** What is the purpose of the `self` parameter in Python class methods?
+
+
+Click to reveal answer
+
+**Answer:** It refers to the instance of the class itself
+
+**Explanation:** `self` allows you to access the attributes and methods of the current object. It's automatically passed when you call a method, so `my_car.start()` is actually `Car.start(my_car)` behind the scenes.
+
+
+---
3. Encapsulation - Hiding the internal details and only exposing necessary parts.
@@ -64,6 +107,41 @@ class BankAccount:
return self.__balance
```
+---
+
+### 🧠 Quiz 2: Encapsulation
+
+**Question 1:** What does the double underscore `__` before a variable name signify in Python?
+
+
+Click to reveal answer
+
+**Answer:** It makes the variable private (name mangling)
+
+**Explanation:** Variables starting with `__` (like `__balance`) are considered private. They cannot be directly accessed from outside the class, providing encapsulation and data protection.
+
+
+**Question 2:** How can you access the private variable `__balance` from outside the class?
+
+
+Click to reveal answer
+
+**Answer:** Through public methods like `get_balance()` or using `_ClassName__variableName`
+
+**Explanation:** The proper way is to use getter methods like `get_balance()`. While you can technically access it using `_BankAccount__balance`, this defeats the purpose of encapsulation and should be avoided.
+
+
+**Question 3:** Why is encapsulation important in OOP?
+
+
+Click to reveal answer
+
+**Answer:** It protects data from unauthorized access and modification, improving security and maintainability
+
+**Explanation:** Encapsulation allows you to control how data is accessed and modified. For example, in a bank account, you can ensure deposits are positive numbers and prevent direct manipulation of the balance.
+
+
+---
4. Inheritance - One class can inherit from another.
@@ -83,6 +161,41 @@ tesla.start()
tesla.battery_info()
```
+---
+
+### 🧠 Quiz 3: Inheritance
+
+**Question 1:** What is the purpose of `super().__init__(brand, model)` in the code above?
+
+
+Click to reveal answer
+
+**Answer:** It calls the parent class's `__init__` method to initialize inherited attributes
+
+**Explanation:** `super()` allows the child class to access methods from the parent class. Here, it initializes `brand` and `model` from the parent `Car` class before adding the new `battery` attribute.
+
+
+**Question 2:** Can an `ElectricCar` object use the `start()` method even though it's not defined in the `ElectricCar` class?
+
+
+Click to reveal answer
+
+**Answer:** Yes, because it inherits the method from the parent `Car` class
+
+**Explanation:** Inheritance allows child classes to use all methods and attributes from the parent class. `ElectricCar` inherits `start()` from `Car`, so `tesla.start()` works perfectly.
+
+
+**Question 3:** What is the parent class in the code example above?
+
+
+Click to reveal answer
+
+**Answer:** `Car`
+
+**Explanation:** `Car` is the parent (or base/super) class, and `ElectricCar` is the child (or derived/sub) class. This is shown by `class ElectricCar(Car):`.
+
+
+---
5. Polymorphism - Same function name, but different behavior depending on the object.
@@ -102,6 +215,58 @@ for pet in pets:
print(pet.speak()) # Woof! / Meow!
```
+---
+
+### 🧠 Quiz 4: Polymorphism
+
+**Question 1:** What does polymorphism mean in OOP?
+
+
+Click to reveal answer
+
+**Answer:** The ability of different objects to respond to the same method call in different ways
+
+**Explanation:** Polymorphism (meaning "many forms") allows different classes to have methods with the same name but different implementations. In the example, both `Dog` and `Cat` have a `speak()` method, but they return different sounds.
+
+
+**Question 2:** What will be the output of this code?
+```python
+class Bird:
+ def move(self):
+ return "Fly"
+
+class Fish:
+ def move(self):
+ return "Swim"
+
+animals = [Bird(), Fish()]
+for animal in animals:
+ print(animal.move())
+```
+
+
+Click to reveal answer
+
+**Answer:**
+```
+Fly
+Swim
+```
+
+**Explanation:** Each object responds to `move()` with its own implementation. The `Bird` object returns "Fly" and the `Fish` object returns "Swim", demonstrating polymorphism.
+
+
+**Question 3:** Why is polymorphism useful in programming?
+
+
+Click to reveal answer
+
+**Answer:** It allows you to write flexible code that can work with objects of different types through a common interface
+
+**Explanation:** Polymorphism lets you write code like `for pet in pets: print(pet.speak())` without knowing the specific type of each pet. This makes code more flexible, maintainable, and easier to extend.
+
+
+---
6. Abstraction - Hiding implementation details, showing only essential features (using abc module).
@@ -126,4 +291,90 @@ c = Circle(5)
print(c.area()) # 78.5
```
+---
+
+### 🧠 Quiz 5: Abstraction and OOP Concepts
+
+**Question 1:** What happens if you try to create an instance of the abstract `Shape` class directly?
+```python
+shape = Shape()
+```
+
+
+Click to reveal answer
+
+**Answer:** It raises a `TypeError` - you cannot instantiate an abstract class
+
+**Explanation:** Abstract classes (created using `ABC`) are meant to be templates that other classes inherit from. They cannot be instantiated directly. You must create a concrete subclass that implements all abstract methods.
+
+
+**Question 2:** What is the purpose of the `@abstractmethod` decorator?
+
+
+Click to reveal answer
+
+**Answer:** It forces child classes to implement that specific method
+
+**Explanation:** When a method is decorated with `@abstractmethod`, any class that inherits from the abstract class MUST provide an implementation for that method. This ensures a consistent interface across all child classes.
+
+
+**Question 3:** Match each OOP principle with its description:
+
+
+Click to reveal answer
+
+**Answer:**
+- **Encapsulation** → Hiding internal data and providing controlled access
+- **Inheritance** → Creating new classes based on existing ones
+- **Polymorphism** → Same method name, different behavior
+- **Abstraction** → Hiding complex implementation details, showing only essential features
+
+**Explanation:** These four pillars of OOP work together to create well-organized, maintainable code:
+- Encapsulation protects data
+- Inheritance promotes code reuse
+- Polymorphism enables flexibility
+- Abstraction simplifies complexity
+
+
+**Question 4:** What would happen if the `Circle` class didn't implement the `area()` method?
+
+
+Click to reveal answer
+
+**Answer:** Python would raise a `TypeError` when trying to instantiate the `Circle` class
+
+**Explanation:** Since `area()` is an abstract method in the parent `Shape` class, any concrete child class must implement it. If `Circle` doesn't provide an implementation, Python won't allow you to create a `Circle` object.
+
+
+**Question 5:** Which OOP principle is demonstrated in this code?
+```python
+class PaymentProcessor:
+ def process(self, amount):
+ pass
+
+class CreditCard(PaymentProcessor):
+ def process(self, amount):
+ print(f"Processing ${amount} via Credit Card")
+
+class PayPal(PaymentProcessor):
+ def process(self, amount):
+ print(f"Processing ${amount} via PayPal")
+```
+
+
+Click to reveal answer
+
+**Answer:** Both Inheritance and Polymorphism
+
+**Explanation:**
+- **Inheritance**: `CreditCard` and `PayPal` inherit from `PaymentProcessor`
+- **Polymorphism**: Both classes have their own implementation of the `process()` method
+This allows you to treat different payment methods uniformly while each processes payments in its own way.
+
+
+---
+
+## Conclusion
+
+Object-Oriented Programming in Python provides powerful tools for organizing code through classes, objects, encapsulation, inheritance, polymorphism, and abstraction. Mastering these concepts enables you to write more maintainable, reusable, and scalable code.
diff --git a/docs/python/python_operators.md b/docs/python/python_operators.md
index 39c968cf..ecae9ab4 100644
--- a/docs/python/python_operators.md
+++ b/docs/python/python_operators.md
@@ -33,6 +33,53 @@ Used to perform basic mathematical operations:
| `%` | Modulus (remainder)| `10 % 3` | `1` |
| `**` | Exponentiation | `2 ** 3` | `8` |
+---
+
+### 🧠 Quiz 1: Arithmetic Operators
+
+**Question 1:** What is the result of `15 // 4`?
+
+
+Click to reveal answer
+
+**Answer:** `3`
+
+**Explanation:** The `//` operator performs floor division, which divides and then rounds down to the nearest integer. 15 divided by 4 is 3.75, which rounds down to 3.
+
+
+**Question 2:** What is the difference between `/` and `//` operators?
+
+
+Click to reveal answer
+
+**Answer:** `/` returns a float (decimal), while `//` returns an integer (floor division)
+
+**Explanation:**
+- `10 / 3` returns `3.3333...` (float)
+- `10 // 3` returns `3` (integer, rounded down)
+
+
+**Question 3:** What will be the output of `7 % 3`?
+
+
+Click to reveal answer
+
+**Answer:** `1`
+
+**Explanation:** The modulus operator `%` returns the remainder after division. 7 divided by 3 is 2 with a remainder of 1.
+
+
+**Question 4:** Calculate: `2 ** 4`
+
+
+Click to reveal answer
+
+**Answer:** `16`
+
+**Explanation:** The `**` operator is used for exponentiation (power). `2 ** 4` means 2 to the power of 4, which is 2 × 2 × 2 × 2 = 16.
+
+
+---
## Comparison Operators
@@ -58,6 +105,55 @@ Used to combine conditional statements here.
| `or` | True if at least one is true | `True or False` | `True` |
| `not` | Reverses the result | `not True` | `False`|
+---
+
+### 🧠 Quiz 2: Comparison and Logical Operators
+
+**Question 1:** What will `10 == 10.0` return?
+
+
+Click to reveal answer
+
+**Answer:** `True`
+
+**Explanation:** Python compares the values, not the types. Since 10 and 10.0 have the same value, the comparison returns `True`. To check if they're the same type, you'd use `type(10) == type(10.0)` which would return `False`.
+
+
+**Question 2:** Evaluate: `(5 > 3) and (10 < 20)`
+
+
+Click to reveal answer
+
+**Answer:** `True`
+
+**Explanation:** Both conditions are true: 5 is greater than 3 AND 10 is less than 20. The `and` operator returns `True` only when both conditions are true.
+
+
+**Question 3:** What is the result of `not (5 > 10)`?
+
+
+Click to reveal answer
+
+**Answer:** `True`
+
+**Explanation:** First, `5 > 10` evaluates to `False`. Then, `not False` becomes `True`. The `not` operator reverses the boolean value.
+
+
+**Question 4:** What will be the output?
+```python
+x = 15
+result = (x > 10) or (x < 5)
+```
+
+
+Click to reveal answer
+
+**Answer:** `True`
+
+**Explanation:** The `or` operator returns `True` if at least one condition is true. Here, `x > 10` is `True` (15 > 10), so the entire expression is `True` without even checking the second condition.
+
+
+---
## Assignment Operators
@@ -74,6 +170,49 @@ Used to assign values to variables.
| `%=` | `x %= 2` | `x = x % 2` |
| `**=` | `x **= 2`| `x = x ** 2` |
+---
+
+### 🧠 Quiz 3: Assignment Operators
+
+**Question 1:** If `x = 10`, what will be the value of `x` after `x += 5`?
+
+
+Click to reveal answer
+
+**Answer:** `15`
+
+**Explanation:** `x += 5` is shorthand for `x = x + 5`. So, 10 + 5 = 15.
+
+
+**Question 2:** What will be the final value of `y`?
+```python
+y = 20
+y -= 8
+y *= 2
+```
+
+
+Click to reveal answer
+
+**Answer:** `24`
+
+**Explanation:**
+- Start: `y = 20`
+- After `y -= 8`: `y = 12` (20 - 8)
+- After `y *= 2`: `y = 24` (12 × 2)
+
+
+**Question 3:** If `a = 7`, what is `a` after `a %= 3`?
+
+
+Click to reveal answer
+
+**Answer:** `1`
+
+**Explanation:** `a %= 3` is equivalent to `a = a % 3`. So, 7 % 3 = 1 (remainder when 7 is divided by 3).
+
+
+---
## Bitwise Operators
@@ -108,7 +247,74 @@ Used to compare the memory location of two objects.
| `is` | Returns `True` if same object | `x is y` | `True` |
| `is not` | Returns `True` if not same object | `x is not y`| `True` |
+---
+
+### 🧠 Quiz 4: Membership and Identity Operators
+
+**Question 1:** What will `3 in [1, 2, 3, 4]` return?
+
+
+Click to reveal answer
+
+**Answer:** `True`
+
+**Explanation:** The `in` operator checks if a value exists in a sequence. Since 3 is present in the list, it returns `True`.
+
+
+**Question 2:** What is the output?
+```python
+text = "Python Programming"
+result = "Java" not in text
+```
+
+
+Click to reveal answer
+
+**Answer:** `True`
+
+**Explanation:** The string "Java" is not found in "Python Programming", so `"Java" not in text` returns `True`.
+
+
+**Question 3:** What's the difference between `==` and `is`?
+
+
+Click to reveal answer
+
+**Answer:** `==` compares values, `is` compares object identity (memory location)
+**Explanation:**
+```python
+a = [1, 2, 3]
+b = [1, 2, 3]
+c = a
+
+a == b # True (same values)
+a is b # False (different objects in memory)
+a is c # True (same object)
+```
+
+
+**Question 4:** What will this code output?
+```python
+x = [1, 2]
+y = [1, 2]
+print(x is y)
+print(x == y)
+```
+
+
+Click to reveal answer
+
+**Answer:**
+```
+False
+True
+```
+
+**Explanation:** `x` and `y` have the same values but are different objects in memory. So `x == y` is `True` (same values) but `x is y` is `False` (different memory locations).
+
+
+---
## Use Cases of Python Operators
@@ -239,6 +445,78 @@ result = 10 + 5 * 2 # ➜ 10 + (5 * 2) = 20
---
+### 🧠 Quiz 5: Real-World Applications
+
+**Question 1:** In the shopping cart example, if price = 200, quantity = 4, and discount = 0.15, what is the final_amount?
+
+
+Click to reveal answer
+
+**Answer:** `680.0`
+
+**Explanation:**
+- Total = 200 × 4 = 800
+- Discount amount = 800 × 0.15 = 120
+- Final amount = 800 - 120 = 680.0
+
+
+**Question 2:** What will this login system print?
+```python
+username = "user"
+password = "1234"
+
+if username == "admin" and password == "1234":
+ print("Login successful")
+else:
+ print("Invalid credentials")
+```
+
+
+Click to reveal answer
+
+**Answer:** `Invalid credentials`
+
+**Explanation:** The `and` operator requires BOTH conditions to be true. While the password is correct, the username is "user" not "admin", so the condition fails.
+
+
+**Question 3:** In the game score example, if the initial score is 50 and we execute `score += 10` twice, what is the final score?
+
+
+Click to reveal answer
+
+**Answer:** `70`
+
+**Explanation:**
+- Start: score = 50
+- First `score += 10`: score = 60
+- Second `score += 10`: score = 70
+
+
+**Question 4:** What is the result of `10 + 5 * 2 ** 2`?
+
+
+Click to reveal answer
+
+**Answer:** `30`
+
+**Explanation:** Following operator precedence (PEMDAS):
+- First: `2 ** 2` = 4 (exponentiation)
+- Then: `5 * 4` = 20 (multiplication)
+- Finally: `10 + 20` = 30 (addition)
+
+
+**Question 5:** In the file permission system, what permission value represents all three permissions (read=4, write=2, execute=1)?
+
+
+Click to reveal answer
+
+**Answer:** `7`
+
+**Explanation:** Using bitwise OR: `4 | 2 | 1 = 7`. This combines all three permissions. This is commonly seen in Unix/Linux file permissions (chmod 777).
+
+
+---
+
## Summary Table
| Operator Type | Example Use Case |
@@ -256,5 +534,4 @@ result = 10 + 5 * 2 # ➜ 10 + (5 * 2) = 20
## Conclusion
-Operators are the core building blocks of logic and calculation in Python. Understanding how they work is crucial to writing effective Python code.
-
+Operators are the core building blocks of logic and calculation in Python. Understanding how they work is crucial to writing effective Python code.
\ No newline at end of file
From 904560fa64567052b0f70cc00e762de4bb099791 Mon Sep 17 00:00:00 2001
From: byteninja01 <186124410+byteninja01@users.noreply.github.com.>
Date: Sat, 4 Oct 2025 17:58:08 +0530
Subject: [PATCH 2/2] fix: resolve MDX syntax error in the files
---
docs/python/python-simple-project.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/python/python-simple-project.md b/docs/python/python-simple-project.md
index 9cba450f..1642095a 100644
--- a/docs/python/python-simple-project.md
+++ b/docs/python/python-simple-project.md
@@ -135,7 +135,7 @@ The `random` module provides functions for generating random numbers, including
Show Answer
**Answer: C) Generates a random integer between 1 and 100 (inclusive)**
-`randint(a, b)` returns a random integer N such that a <= N <= b, so both endpoints are included.
+`randint(a, b)` returns a random integer N such that `a <= N <= b`, so both endpoints are included.
---