Python basic to advance
43 subscribers
131 photos
98 links
Learn Python daily with bite-sized lessons, code examples, output previews, and fun quizzes. Perfect for beginners and enthusiasts!
Download Telegram
Day 21: Decorators \- Code & Output
Hello Python Learners! πŸ‘‹

It's **Day 22** of our Python journey, and today we're diving into some advanced but incredibly useful concepts: **Generators and Iterators**! πŸš€

Ever wondered how Python handles huge datasets without hogging all your memory? That's where these come in! Iterators give you a way to access elements one by one, while generators are a super-convenient way to create iterators, making your code more memory-efficient and performant by generating values on-the-fly, only when needed. Think "lazy evaluation" – powerful stuff! Let's see them in action. πŸ‘‡

```python
# --- Understanding Iterators ---
# An iterator is an object that can be iterated upon.
# It implements __iter__() and __next__() methods.
class MyRangeIterator:
def __init__(self, start, end):
self.current = start
self.end = end

def __iter__(self):
return self

def __next__(self):
if self.current <= self.end:
value = self.current
self.current += 1
return value
else:
raise StopIteration

print("--- Custom Iterator (MyRangeIterator) ---")
for num in MyRangeIterator(1, 3):
print(f"Iterator value: {num}")

# --- Understanding Generators ---
# Generators are functions that use the 'yield' keyword.
# They produce a sequence of results instead of a single value.
# Each 'yield' statement pauses the function and saves its state.
def simple_generator(start, end):
current = start
while current <= end:
yield current # Pause here, return 'current', resume later
current += 1

print("n--- Simple Generator ---")
gen_obj = simple_generator(1, 3)
print(f"Generator object: {gen_obj}")
for val in gen_obj:
print(f"Generator value: {val}")

# Another common use: infinite sequences or data streams
def infinite_sequence():
num = 0
while True:
yield num
num += 1

print("n--- Infinite Sequence (first 5) ---")
inf_gen = infinite_sequence()
for _ in range(5):
print(f"Infinite value: {next(inf_gen)}")

```

Output example:
```
--- Custom Iterator (MyRangeIterator) ---
Iterator value: 1
Iterator value: 2
Iterator value: 3

--- Simple Generator ---
Generator object: <generator object simple_generator at 0x...>
Generator value: 1
Generator value: 2
Generator value: 3

--- Infinite Sequence (first 5) ---
Infinite value: 0
Infinite value: 1
Infinite value: 2
Infinite value: 3
Infinite value: 4
```

πŸ”₯ **Small Challenge:**
Modify the `simple_generator` function to yield only **even numbers** within a given range. Or, create a generator that yields the first `n` terms of the Fibonacci sequence! Share your code! πŸ§‘β€πŸ’»

---
Day 22: Generators and Iterators \- Code & Output
✨🐍 **Python Learning Series - Day 23: Context Managers (with statement)!** πŸ“š

Today, we're diving into the incredibly powerful `with` statement! πŸ’ͺ This Pythonic gem simplifies resource management, ensuring that resources like files, network connections, or database locks are properly acquired and, most importantly, *reliably released*, even if errors occur. No more forgotten `.close()` calls! The `with` statement handles the setup (`__enter__`) and teardown (`__exit__`) for you, making your code cleaner and safer. βœ… Let's explore!

```python
print("--- File Handling Example ---")
file_name = "day23_notes.txt"

try:
# Using 'with' to open a file for writing
# The file will be automatically closed when the 'with' block exits
with open(file_name, "w") as f:
f.write("Day 23: Exploring Python Context Managers.n")
f.write("The 'with' statement simplifies resource management significantly!n")
print(f"Content successfully written to '{file_name}'.")

# Using 'with' to open a file for reading
with open(file_name, "r") as f:
content = f.read()
print(f"nContent read from '{file_name}':")
print(content.strip())

except IOError as e:
print(f"An error occurred while accessing the file: {e}")
```

Output example:
```
--- File Handling Example ---
Content successfully written to 'day23_notes.txt'.

Content read from 'day23_notes.txt':
Day 23: Exploring Python Context Managers.
The 'with' statement simplifies resource management significantly!
```

πŸ’‘ **Your Challenge:**
Create a *simple custom context manager* using a class with `__enter__` and `__exit__` methods. Make `__enter__` print "Resource acquired!" and `__exit__` print "Resource released!". Then, use it with a `with` statement and show the output! πŸ‘‡

---
Day 23: Context Managers \(with statement\) \- Code & Output
Here's your Telegram post for Day 24:

---

πŸš€ Python Learning Series - Day 24! ✨ Today, we're unlocking two incredibly powerful and Pythonic features: Lambda Functions and List Comprehensions! πŸ’‘ Lambda functions let you create small, anonymous, single-expression functions on the fly. List comprehensions offer a concise and efficient way to create lists. Master these, and your Python code will become much cleaner and more efficient! Let's dive in! πŸ“š

```python
# Day 24: Lambda Functions & List Comprehensions

# --- Lambda Functions ---
# 1. Simple lambda function
add_five = lambda x: x + 5
print(f"Lambda: 10 + 5 = {add_five(10)}")

# 2. Using lambda with map() to apply a function to all items
numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(lambda x: x**2, numbers))
print(f"Map with lambda (squared): {squared_numbers}")

# 3. Using lambda with filter() to select items
filtered_list = list(filter(lambda x: x > 3, numbers))
print(f"Filter with lambda (numbers > 3): {filtered_list}")

# --- List Comprehensions ---
# 4. Basic list comprehension (squares)
squares_lc = [x**2 for x in range(1, 6)]
print(f"List comprehension (squares 1-5): {squares_lc}")

# 5. List comprehension with a condition (even numbers)
even_numbers_lc = [num for num in numbers if num % 2 == 0]
print(f"List comprehension (even numbers): {even_numbers_lc}")
```

Output example:
```
Lambda: 10 + 5 = 15
Map with lambda (squared): [1, 4, 9, 16, 25]
Filter with lambda (numbers > 3): [4, 5]
List comprehension (squares 1-5): [1, 4, 9, 16, 25]
List comprehension (even numbers): [2, 4]
```

πŸ’‘ Small Challenge: From our `numbers` list, create a new list containing only numbers greater than 2, and then cube each of those numbers. Try to achieve this in a single line using a *list comprehension*! Can you also think about how `map` and `filter` with *lambda functions* could achieve the same result?

---
Day 24: Lambda Functions and List Comprehensions \- Code & Output
Which of the following is the most concise and Pythonic way to create a list containing the squares of numbers from 0 to 4 (i.e., [0, 1, 4, 9, 16])?
Anonymous Quiz
0%
[x**2 for x in range(5)]
0%
squares = [] for x in range(5): squares.append(x**2)
0%
list(map(lambda x: x**2, range(5)))
0%
[x*x for x in [0, 1, 2, 3, 4]]
Here's your Telegram post for Day 25: Unit Testing!

---

πŸš€ Day 25: Master Unit Testing in Python! πŸš€

Ever wondered how to ensure your code works perfectly every time? πŸ€” Today, we dive into Unit Testing – a crucial practice for writing robust and bug-free applications. We'll use Python's built-in `unittest` module to create and run tests for our functions. Get ready to build confidence in your code! πŸ’ͺ Let's make our programs reliable! ✨

First, create a file named `calculator.py`:
```python
# calculator.py
def add(a, b):
"""Adds two numbers and returns the result."""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both inputs must be numbers.")
return a + b

def subtract(a, b):
"""Subtracts b from a and returns the result."""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both inputs must be numbers.")
return a - b
```

Now, create a file named `test_calculator.py` in the same directory:
```python
# test_calculator.py
import unittest
from calculator import add, subtract

class TestCalculator(unittest.TestCase):

def test_add(self):
"""Test the add function with various valid inputs."""
self.assertEqual(add(1, 2), 3)
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(-1, -1), -2)
self.assertEqual(add(0, 0), 0)
self.assertAlmostEqual(add(0.1, 0.2), 0.3)

def test_subtract(self):
"""Test the subtract function with various valid inputs."""
self.assertEqual(subtract(5, 3), 2)
self.assertEqual(subtract(3, 5), -2)
self.assertEqual(subtract(0, 0), 0)
self.assertEqual(subtract(-1, -1), 0)
self.assertEqual(subtract(10, 0), 10)

def test_add_type_error(self):
"""Test add function for TypeError with invalid inputs."""
with self.assertRaises(TypeError):
add(1, "2")
with self.assertRaises(TypeError):
add("a", 2)
with self.assertRaises(TypeError):
add(None, 5)

def test_subtract_type_error(self):
"""Test subtract function for TypeError with invalid inputs."""
with self.assertRaises(TypeError):
subtract(1, "2")
with self.assertRaises(TypeError):
subtract("a", 2)

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

To run the tests, navigate to the directory containing these files in your terminal and execute:
`python -m unittest test_calculator.py`

Output example:
```
...
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK
```

πŸ’‘ **Your Challenge:**
Modify `calculator.py` to include a `multiply(a, b)` function. Don't forget to add type checking! Then, add a new test method `test_multiply` within `TestCalculator` in `test_calculator.py` to thoroughly test your new function with both valid and invalid inputs! Share your solution in the comments! πŸ‘‡

---
Day 25: Unit Testing \- Code & Output
πŸŽ‰ **Day 26: Basic Web Scraping with Beautiful Soup** πŸŽ‰

Today, we unlock the power of collecting data directly from websites! πŸ•ΈοΈ Web scraping is a valuable skill, and Python's `BeautifulSoup` library makes it incredibly easy. We'll learn how to parse HTML content and extract specific information like titles, paragraphs, or links. Get ready to dive into the web's structure and pull out the data you need! πŸ’»βœ¨

```python
import requests
from bs4 import BeautifulSoup

# For this example, let's use a simple local HTML string.
# In a real scenario, you'd use requests.get('URL').text
html_doc = """
<html>
<head>
<title>Learning Beautiful Soup</title>
</head>
<body>
<h1>Web Scraping Fundamentals</h1>
<p class="intro">Beautiful Soup helps parse HTML and XML documents.</p>
<a href="https://www.python.org">Python Official Website</a>
<p>Another paragraph.</p>
</body>
</html>
"""

# Create a BeautifulSoup object
soup = BeautifulSoup(html_doc, 'html.parser')

# 1. Get the title of the page
page_title = soup.title.string
print(f"Page Title: {page_title}")

# 2. Find the first h1 tag
h1_tag = soup.find('h1')
print(f"H1 Content: {h1_tag.text}")

# 3. Find a paragraph with a specific class
intro_paragraph = soup.find('p', class_='intro')
print(f"Intro Paragraph: {intro_paragraph.text}")

# 4. Find the first link (a tag)
first_link = soup.find('a')
print(f"First Link Text: {first_link.text}")
print(f"First Link URL: {first_link['href']}")
```

Output example:
```
Page Title: Learning Beautiful Soup
H1 Content: Web Scraping Fundamentals
Intro Paragraph: Beautiful Soup helps parse HTML and XML documents.
First Link Text: Python Official Website
First Link URL: https://www.python.org
```

### Small Challenge πŸš€
Try to find *all* the `<p>` tags in the `html_doc` string using `soup.find_all('p')` and print their text content! Can you also extract the `href` attribute from the `<a>` tag using `first_link.get('href')`?

---
Day 26: Basic Web Scraping \(Beautiful Soup\) \- Code & Output
πŸš€ Day 27: Diving into Web Development with Python! 🌐 Today, we're getting a sneak peek at the powerful world of Python web frameworks: Flask and Django! ✨ These frameworks are your go-to tools for building dynamic and interactive web applications, from simple APIs to complex websites. Flask is known for its simplicity and flexibility (a microframework), while Django offers a full-stack solution with everything included. Let's write our first tiny Flask app! πŸ‘‡

```python
# First, if you haven't, install Flask:
# pip install Flask

from flask import Flask

# Create a Flask application instance
app = Flask(__name__)

# Define a route for the homepage ('/')
@app.route('/')
def hello_world():
return 'Hello, Flask World! πŸ‘‹'

# Run the application if this script is executed directly
if __name__ == '__main__':
# debug=True allows for automatic reloading and provides a debugger
# For production, set debug=False
app.run(debug=True)
```

Output example:
To run this:
1. Save the code as `app.py` (or any `.py` file).
2. Open your terminal in the same directory.
3. Run: `python app.py`

You'll see output like:
```
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: XXX-XXX-XXX
```
Open `http://127.0.0.1:5000/` in your browser to see "Hello, Flask World! πŸ‘‹"

πŸ’‘ **Challenge:** Add another route to the Flask application! Create a new route `@app.route('/greet/<name>')` that takes a name as a URL parameter (e.g., `/greet/Alice`) and returns a personalized greeting like `Hello, Alice!` Try it out! πŸ§‘β€πŸ’»

---
Day 27: Introduction to Flask/Django \- Code & Output
πŸš€ Day 28: Dive into the fascinating world of Asynchronous Programming with `asyncio`! 🐒 Tired of your program waiting? `asyncio` lets your Python code perform multiple tasks *seemingly* at once, especially great for I/O-bound operations like network requests or file operations. Say goodbye to blocking and hello to efficiency! πŸ’¨ Let's make your code run smarter, not harder.

```python
import asyncio
import time

async def perform_task(name, delay):
"""Simulates an I/O-bound operation."""
print(f"[{name}] Starting...")
await asyncio.sleep(delay) # Yield control back to the event loop
print(f"[{name}] Finished after {delay} seconds!")
return f"{name} done"

async def main():
start_time = time.time()
print("Main: Kicking off async tasks...")

# Run tasks concurrently
results = await asyncio.gather(
perform_task("Task A", 2),
perform_task("Task B", 1),
perform_task("Task C", 3)
)

end_time = time.time()
print(f"nMain: All tasks completed in {end_time - start_time:.2f} seconds.")
print(f"Main: Results: {results}")

if __name__ == "__main__":
asyncio.run(main())
```

Output example:
Main: Kicking off async tasks...
[Task A] Starting...
[Task B] Starting...
[Task C] Starting...
[Task B] Finished after 1 seconds!
[Task A] Finished after 2 seconds!
[Task C] Finished after 3 seconds!

Main: All tasks completed in 3.00 seconds.
Main: Results: ['Task A done', 'Task B done', 'Task C done']

πŸ’‘ **Small Challenge:** Add another `async` function that attempts to read a (non-existent) file using `asyncio.to_thread` to simulate a blocking file read. How would you integrate it into the `asyncio.gather` call? What's the benefit of `asyncio.to_thread` here?

---
Day 28: Asynchronous Programming \(asyncio\) \- Code & Output
Here's your Telegram post for Day 29:

---

**Day 29: Basic Database Interaction (SQLite) πŸ’Ύ**

Ever wondered how applications store all that important information? Today, we dive into the world of databases with Python's built-in `sqlite3` module! πŸš€ SQLite is a super lightweight, file-based database – perfect for learning and small projects. You'll learn to connect, create tables, insert data, and retrieve it. Let's get persistent! ✨

```python
import sqlite3

# 1. Connect to the database (or create it if it doesn't exist)
# This will create a file named 'my_items.db' in your script's directory.
conn = sqlite3.connect('my_items.db')
cursor = conn.cursor()

# 2. Create a table (IF NOT EXISTS prevents errors if you run this multiple times)
cursor.execute('''
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
item_name TEXT NOT NULL,
quantity INTEGER NOT NULL
)
''')

# 3. Insert some data into the table
# Using placeholders (?) is crucial to prevent SQL injection vulnerabilities.
cursor.execute("INSERT INTO products (item_name, quantity) VALUES (?, ?)", ("Laptop", 5))
cursor.execute("INSERT INTO products (item_name, quantity) VALUES (?, ?)", ("Mouse", 12))
cursor.execute("INSERT INTO products (item_name, quantity) VALUES (?, ?)", ("Keyboard", 8))
cursor.execute("INSERT INTO products (item_name, quantity) VALUES (?, ?)", ("Monitor", 3))


# Important: Commit the changes to save them permanently to the database file
conn.commit()

# 4. Retrieve and print all data from the 'products' table
print("All products in the database:")
cursor.execute("SELECT * FROM products")
items = cursor.fetchall() # Fetches all rows from the last executed query

for item in items:
print(item)

# 5. Close the database connection
conn.close()
```

Output example:
```
All products in the database:
(1, 'Laptop', 5)
(2, 'Mouse', 12)
(3, 'Keyboard', 8)
(4, 'Monitor', 3)
```

Challenge Time! 🧠 Can you modify the `SELECT` query in the code to retrieve *only* products with a `quantity` greater than 10? Share your solution! πŸ‘‡

---