Here's your Telegram post for Day 26: Basic Web Scraping with Beautiful Soup!
---
π Day 26: Basic Web Scraping (Beautiful Soup) πΈοΈ
Today, we're diving into the exciting world of web scraping! β¨ Learn how to extract information from websites using Python's fantastic Beautiful Soup library. Beautiful Soup makes parsing HTML and XML documents a breeze, allowing you to navigate, search, and modify the parse tree. It's an essential tool for data collection, research, and automating tasks. Let's start with a simple example!
```python
from bs4 import BeautifulSoup
# A simple HTML string for demonstration purposes
html_doc = """
<html>
<head>
<title>Beautiful Soup Demo Page</title>
</head>
<body>
<h1>Welcome to Web Scraping!</h1>
<p class="intro">This is an introduction to Beautiful Soup.</p>
<a href="https://www.python.org">Python Website</a>
<div id="footer">
<p>Copyright 2023</p>
</div>
</body>
</html>
"""
# Create a BeautifulSoup object
soup = BeautifulSoup(html_doc, 'html.parser')
# Extract the page title
title_tag = soup.title
print(f"Page Title: {title_tag.string}")
# Extract the text from the h1 tag
h1_tag = soup.h1
print(f"H1 Content: {h1_tag.string}")
# Find a specific paragraph by class
intro_paragraph = soup.find('p', class_='intro')
print(f"Intro Paragraph: {intro_paragraph.text}")
```
Output example:
```
Page Title: Beautiful Soup Demo Page
H1 Content: Welcome to Web Scraping!
Intro Paragraph: This is an introduction to Beautiful Soup.
```
π **Challenge:**
Can you modify the code to extract the `href` attribute from the `<a>` tag and the text content from the `div` with `id="footer"`? Share your solutions!
---
---
π Day 26: Basic Web Scraping (Beautiful Soup) πΈοΈ
Today, we're diving into the exciting world of web scraping! β¨ Learn how to extract information from websites using Python's fantastic Beautiful Soup library. Beautiful Soup makes parsing HTML and XML documents a breeze, allowing you to navigate, search, and modify the parse tree. It's an essential tool for data collection, research, and automating tasks. Let's start with a simple example!
```python
from bs4 import BeautifulSoup
# A simple HTML string for demonstration purposes
html_doc = """
<html>
<head>
<title>Beautiful Soup Demo Page</title>
</head>
<body>
<h1>Welcome to Web Scraping!</h1>
<p class="intro">This is an introduction to Beautiful Soup.</p>
<a href="https://www.python.org">Python Website</a>
<div id="footer">
<p>Copyright 2023</p>
</div>
</body>
</html>
"""
# Create a BeautifulSoup object
soup = BeautifulSoup(html_doc, 'html.parser')
# Extract the page title
title_tag = soup.title
print(f"Page Title: {title_tag.string}")
# Extract the text from the h1 tag
h1_tag = soup.h1
print(f"H1 Content: {h1_tag.string}")
# Find a specific paragraph by class
intro_paragraph = soup.find('p', class_='intro')
print(f"Intro Paragraph: {intro_paragraph.text}")
```
Output example:
```
Page Title: Beautiful Soup Demo Page
H1 Content: Welcome to Web Scraping!
Intro Paragraph: This is an introduction to Beautiful Soup.
```
π **Challenge:**
Can you modify the code to extract the `href` attribute from the `<a>` tag and the text content from the `div` with `id="footer"`? Share your solutions!
---
Python.org
Welcome to Python.org
The official home of the Python Programming Language
What is the primary purpose of the 'html.parser' argument when creating a Beautiful Soup object?
Anonymous Quiz
0%
It specifies the parser Beautiful Soup should use to interpret the HTML document.
0%
It indicates that the input document is plain text, not HTML.
0%
It tells Beautiful Soup to output only valid HTML tags.
0%
It defines the version of HTML being parsed (e.g., HTML5, XHTML).
Here's your Telegram post and quiz:
---
π Day 27: Web Wonders! π Ready to build your first web app? Today, we're diving into the exciting world of Python web frameworks with an introduction to Flask and Django! These powerful tools let you create dynamic websites, APIs, and more. We'll start with a super simple Flask example to get your feet wet and see how easy it is to serve content directly from Python. Let's make some magic happen! β¨
```python
from flask import Flask
# Create a Flask application instance
app = Flask(__name__)
# Define a route for the home page ('/')
@app.route('/')
def hello_world():
"""Returns a simple 'Hello, World!' message when the home page is accessed."""
return "Hello from Flask, Pythonista! π"
# Run the application if this script is executed directly
if __name__ == '__main__':
# debug=True enables the debugger and auto-reloader, great for development
app.run(debug=True)
```
Output example:
(When you run the script, open your browser and navigate to `http://127.0.0.1:5000/`)
*Console output when running `python your_script_name.py`:*
```
* 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
```
*What you'll see in your browser:*
```
Hello from Flask, Pythonista! π
```
### Small challenge:
β¨ Try changing the message returned by `hello_world()` to something else, like `return "Welcome to my first web page with Flask!"`. Can you also add another route, say `/about`, that returns information about yourself or the learning series? Hint: You'll need another `@app.route('/about')` decorator and a new function. Good luck! π‘
---
---
π Day 27: Web Wonders! π Ready to build your first web app? Today, we're diving into the exciting world of Python web frameworks with an introduction to Flask and Django! These powerful tools let you create dynamic websites, APIs, and more. We'll start with a super simple Flask example to get your feet wet and see how easy it is to serve content directly from Python. Let's make some magic happen! β¨
```python
from flask import Flask
# Create a Flask application instance
app = Flask(__name__)
# Define a route for the home page ('/')
@app.route('/')
def hello_world():
"""Returns a simple 'Hello, World!' message when the home page is accessed."""
return "Hello from Flask, Pythonista! π"
# Run the application if this script is executed directly
if __name__ == '__main__':
# debug=True enables the debugger and auto-reloader, great for development
app.run(debug=True)
```
Output example:
(When you run the script, open your browser and navigate to `http://127.0.0.1:5000/`)
*Console output when running `python your_script_name.py`:*
```
* 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
```
*What you'll see in your browser:*
```
Hello from Flask, Pythonista! π
```
### Small challenge:
β¨ Try changing the message returned by `hello_world()` to something else, like `return "Welcome to my first web page with Flask!"`. Can you also add another route, say `/about`, that returns information about yourself or the learning series? Hint: You'll need another `@app.route('/about')` decorator and a new function. Good luck! π‘
---
Here's your Telegram post and quiz question!
---
π Day 28: Diving into Asynchronous Programming with `asyncio`! π
Today, we're tackling one of Python's most powerful features for efficient, non-blocking I/O operations and concurrency: `asyncio`! π‘ Ever wondered how to make your programs run multiple tasks seemingly at once without getting stuck? `asyncio` is your answer! We'll explore `async` and `await` to write concurrent code that scales. Let's make our Python faster and more responsive! β±οΈ
```python
import asyncio
import time
async def fetch_data(delay, item_name):
"""Simulates an asynchronous I/O operation like fetching data from a network."""
print(f"β³ {item_name}: Starting data fetch (will take {delay}s)...")
await asyncio.sleep(delay) # Non-blocking pause
print(f"β {item_name}: Data fetched!")
return f"Data for {item_name}"
async def main():
start_time = time.monotonic()
print("π Main program started!")
# Run multiple coroutines concurrently
results = await asyncio.gather(
fetch_data(3, "User Profile"),
fetch_data(1, "Product List"),
fetch_data(2, "Order History")
)
end_time = time.monotonic()
print("--- All fetches completed! ---")
for res in results:
print(f"Retrieved: {res}")
print(f"Total time taken: {end_time - start_time:.2f} seconds.")
if __name__ == "__main__":
asyncio.run(main())
```
Output example:
```
π Main program started!
β³ User Profile: Starting data fetch (will take 3s)...
β³ Product List: Starting data fetch (will take 1s)...
β³ Order History: Starting data fetch (will take 2s)...
β Product List: Data fetched!
β Order History: Data fetched!
β User Profile: Data fetched!
--- All fetches completed! ---
Retrieved: Data for User Profile
Retrieved: Data for Product List
Retrieved: Data for Order History
Total time taken: 3.00 seconds.
```
Challenge: π€
Modify the `main` function to use `asyncio.create_task()` for each `fetch_data` call instead of `asyncio.gather()`. How would you then collect their results? Share your solution! βοΈ
---
---
π Day 28: Diving into Asynchronous Programming with `asyncio`! π
Today, we're tackling one of Python's most powerful features for efficient, non-blocking I/O operations and concurrency: `asyncio`! π‘ Ever wondered how to make your programs run multiple tasks seemingly at once without getting stuck? `asyncio` is your answer! We'll explore `async` and `await` to write concurrent code that scales. Let's make our Python faster and more responsive! β±οΈ
```python
import asyncio
import time
async def fetch_data(delay, item_name):
"""Simulates an asynchronous I/O operation like fetching data from a network."""
print(f"β³ {item_name}: Starting data fetch (will take {delay}s)...")
await asyncio.sleep(delay) # Non-blocking pause
print(f"β {item_name}: Data fetched!")
return f"Data for {item_name}"
async def main():
start_time = time.monotonic()
print("π Main program started!")
# Run multiple coroutines concurrently
results = await asyncio.gather(
fetch_data(3, "User Profile"),
fetch_data(1, "Product List"),
fetch_data(2, "Order History")
)
end_time = time.monotonic()
print("--- All fetches completed! ---")
for res in results:
print(f"Retrieved: {res}")
print(f"Total time taken: {end_time - start_time:.2f} seconds.")
if __name__ == "__main__":
asyncio.run(main())
```
Output example:
```
π Main program started!
β³ User Profile: Starting data fetch (will take 3s)...
β³ Product List: Starting data fetch (will take 1s)...
β³ Order History: Starting data fetch (will take 2s)...
β Product List: Data fetched!
β Order History: Data fetched!
β User Profile: Data fetched!
--- All fetches completed! ---
Retrieved: Data for User Profile
Retrieved: Data for Product List
Retrieved: Data for Order History
Total time taken: 3.00 seconds.
```
Challenge: π€
Modify the `main` function to use `asyncio.create_task()` for each `fetch_data` call instead of `asyncio.gather()`. How would you then collect their results? Share your solution! βοΈ
---
What is the primary purpose of the `await` keyword in Python's `asyncio`?
Anonymous Quiz
0%
A. To define an asynchronous function.
0%
B. To pause the execution of a coroutine until another awaitable completes.
0%
C. To explicitly run the asyncio event loop.
0%
D. To declare a variable as asynchronous.
Here's your Telegram post for Day 29!
---
**Python Learning Series - Day 29: Basic Database Interaction (SQLite)**
Day 29 is here! π Ever wondered how your Python apps can store and retrieve data persistently? Today, we dive into the world of databases with SQLite! πΎ We'll learn the basics of connecting, creating tables, inserting data, and fetching results using Python's built-in `sqlite3` module. Get ready to give your scripts a memory! π§ π»
```python
import sqlite3
# 1. Connect to an SQLite database (or create one if it doesn't exist)
# Using 'my_app_data.db' will create a file. ':memory:' would create an in-memory DB.
conn = sqlite3.connect('my_app_data.db')
cursor = conn.cursor()
# 2. Create a table
# IF NOT EXISTS prevents error if table already exists
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER
)
''')
# 3. Insert some data
# Using '?' as placeholders for parameters for safety and convenience
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 30))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Bob", 24))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Charlie", 35))
# 4. Commit changes (save them to the database file)
conn.commit()
# 5. Fetch data
print("All users:")
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall() # Fetch all results
for row in rows:
print(row)
# 6. Close the connection
conn.close()
print("nDatabase operations completed. 'my_app_data.db' created/updated.")
```
Output example:
```
All users:
(1, 'Alice', 30)
(2, 'Bob', 24)
(3, 'Charlie', 35)
Database operations completed. 'my_app_data.db' created/updated.
```
π‘ **Challenge:**
Modify the code to:
1. Insert one more user into the `users` table.
2. Select and print only users whose age is greater than 25.
Share your solution or just let us know you tried! π
---
---
**Python Learning Series - Day 29: Basic Database Interaction (SQLite)**
Day 29 is here! π Ever wondered how your Python apps can store and retrieve data persistently? Today, we dive into the world of databases with SQLite! πΎ We'll learn the basics of connecting, creating tables, inserting data, and fetching results using Python's built-in `sqlite3` module. Get ready to give your scripts a memory! π§ π»
```python
import sqlite3
# 1. Connect to an SQLite database (or create one if it doesn't exist)
# Using 'my_app_data.db' will create a file. ':memory:' would create an in-memory DB.
conn = sqlite3.connect('my_app_data.db')
cursor = conn.cursor()
# 2. Create a table
# IF NOT EXISTS prevents error if table already exists
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER
)
''')
# 3. Insert some data
# Using '?' as placeholders for parameters for safety and convenience
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 30))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Bob", 24))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Charlie", 35))
# 4. Commit changes (save them to the database file)
conn.commit()
# 5. Fetch data
print("All users:")
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall() # Fetch all results
for row in rows:
print(row)
# 6. Close the connection
conn.close()
print("nDatabase operations completed. 'my_app_data.db' created/updated.")
```
Output example:
```
All users:
(1, 'Alice', 30)
(2, 'Bob', 24)
(3, 'Charlie', 35)
Database operations completed. 'my_app_data.db' created/updated.
```
π‘ **Challenge:**
Modify the code to:
1. Insert one more user into the `users` table.
2. Select and print only users whose age is greater than 25.
Share your solution or just let us know you tried! π
---
Which method of a `cursor` object is used to execute SQL commands in Python's `sqlite3` module?
Anonymous Quiz
0%
execute()
0%
run_sql()
0%
query()
0%
perform()
π₯³ *Congratulations on reaching Day 30 of our Python series!* π
Today is all about gearing up for your Final Project! π It's time to consolidate everything you've learned into something truly your own. We'll explore how to brainstorm ideas and review key concepts we've covered. Don't be overwhelmed β think of it as a creative challenge! Let's get those project ideas flowing! π‘
---
Here's a little helper to get your brainstorming started:
```python
import random
def generate_project_idea():
categories = [
"Web Application",
"Command-Line Tool",
"Data Analysis Script",
"Game",
"Automation Script",
"Desktop Application",
"API Integration"
]
themes = [
"personal finance tracker",
"to-do list manager",
"simple calculator",
"weather app",
"file organizer",
"quiz game",
"recipe book",
"contact manager"
]
technologies = [
"using Flask",
"with SQLite database",
"fetching data from an API",
"using object-oriented programming",
"with a GUI library like Tkinter",
"using Pandas for data manipulation"
]
idea_category = random.choice(categories)
idea_theme = random.choice(themes)
idea_tech = random.choice(technologies)
return f"π‘ Your project idea: A {idea_category} that is a {idea_theme} {idea_tech}."
if __name__ == "__main__":
print(generate_project_idea())
```
Output example:
```
π‘ Your project idea: A Web Application that is a recipe book using Flask.
```
---
**Your Challenge:**
1. What's one project idea you've been thinking about building with Python? Share it!
2. Expand the `generate_project_idea` function to include more categories, themes, or even suggest specific Python libraries to use. π
---
Today is all about gearing up for your Final Project! π It's time to consolidate everything you've learned into something truly your own. We'll explore how to brainstorm ideas and review key concepts we've covered. Don't be overwhelmed β think of it as a creative challenge! Let's get those project ideas flowing! π‘
---
Here's a little helper to get your brainstorming started:
```python
import random
def generate_project_idea():
categories = [
"Web Application",
"Command-Line Tool",
"Data Analysis Script",
"Game",
"Automation Script",
"Desktop Application",
"API Integration"
]
themes = [
"personal finance tracker",
"to-do list manager",
"simple calculator",
"weather app",
"file organizer",
"quiz game",
"recipe book",
"contact manager"
]
technologies = [
"using Flask",
"with SQLite database",
"fetching data from an API",
"using object-oriented programming",
"with a GUI library like Tkinter",
"using Pandas for data manipulation"
]
idea_category = random.choice(categories)
idea_theme = random.choice(themes)
idea_tech = random.choice(technologies)
return f"π‘ Your project idea: A {idea_category} that is a {idea_theme} {idea_tech}."
if __name__ == "__main__":
print(generate_project_idea())
```
Output example:
```
π‘ Your project idea: A Web Application that is a recipe book using Flask.
```
---
**Your Challenge:**
1. What's one project idea you've been thinking about building with Python? Share it!
2. Expand the `generate_project_idea` function to include more categories, themes, or even suggest specific Python libraries to use. π
---
When starting a new Python final project, which of the following is the MOST crucial first step?
Anonymous Quiz
0%
Immediately writing complex functions and classes.
0%
Selecting a project idea and clearly defining its scope and features.
0%
Choosing the most advanced and trending libraries available.
0%
Designing a highly detailed user interface before writing any backend logic.
π Day 31: Ready to peer into Python's soul? π΅οΈββοΈ Today, we're diving deep into the **Abstract Syntax Tree (AST)**! Forget just writing code; we'll learn how Python *sees* your code at its structural core. Understanding ASTs empowers you to build incredible tools: custom linters, code generators, refactoring scripts, and even new language features! β¨ This is pure metaprogramming magic. Let's crack open the parser! π οΈ
```python
import ast
# Sample Python code to analyze
code_snippet = """
def greet(name):
print(f"Hello, {name}!")
class MyClass:
def __init__(self, value):
self.value = value
def display(self):
return f"Value: {self.value}"
def calculate_sum(a, b):
# This is a comment
return a + b
"""
class NodeCounter(ast.NodeVisitor):
"""
A simple AST visitor to count different types of nodes
and find function names.
"""
def __init__(self):
self.node_counts = {}
self.function_names = []
def visit(self, node):
# Count all node types
node_type = type(node).__name__
self.node_counts[node_type] = self.node_counts.get(node_type, 0) + 1
# Specific action for FunctionDef
if isinstance(node, ast.FunctionDef):
self.function_names.append(node.name)
self.generic_visit(node) # Continue visiting children
# Parse the code into an AST
tree = ast.parse(code_snippet)
# Create an instance of our visitor and traverse the tree
counter = NodeCounter()
counter.visit(tree)
print("π AST Analysis Results:")
print("-----------------------")
print("Detected Function Names:")
for func_name in counter.function_names:
print(f"- {func_name}")
print("nNode Type Counts:")
for node_type, count in sorted(counter.node_counts.items()):
print(f"- {node_type}: {count}")
```
Output example:
```
π AST Analysis Results:
-----------------------
Detected Function Names:
- greet
- __init__
- display
- calculate_sum
Node Type Counts:
- Arg: 4
- Arguments: 4
- Assign: 1
- Attribute: 1
- BinOp: 1
- Call: 1
- ClassDef: 1
- Constant: 3
- Expr: 1
- FString: 1
- FormattedValue: 1
- FunctionDef: 4
- Load: 8
- Module: 1
- Name: 12
- Return: 2
- Store: 1
- With: 0
```
### Small Challenge π§
Can you modify the `NodeCounter` class to also find and list all `ClassDef` names present in the `code_snippet`? Hint: You'll need to look for `ast.ClassDef` nodes!
---
```python
import ast
# Sample Python code to analyze
code_snippet = """
def greet(name):
print(f"Hello, {name}!")
class MyClass:
def __init__(self, value):
self.value = value
def display(self):
return f"Value: {self.value}"
def calculate_sum(a, b):
# This is a comment
return a + b
"""
class NodeCounter(ast.NodeVisitor):
"""
A simple AST visitor to count different types of nodes
and find function names.
"""
def __init__(self):
self.node_counts = {}
self.function_names = []
def visit(self, node):
# Count all node types
node_type = type(node).__name__
self.node_counts[node_type] = self.node_counts.get(node_type, 0) + 1
# Specific action for FunctionDef
if isinstance(node, ast.FunctionDef):
self.function_names.append(node.name)
self.generic_visit(node) # Continue visiting children
# Parse the code into an AST
tree = ast.parse(code_snippet)
# Create an instance of our visitor and traverse the tree
counter = NodeCounter()
counter.visit(tree)
print("π AST Analysis Results:")
print("-----------------------")
print("Detected Function Names:")
for func_name in counter.function_names:
print(f"- {func_name}")
print("nNode Type Counts:")
for node_type, count in sorted(counter.node_counts.items()):
print(f"- {node_type}: {count}")
```
Output example:
```
π AST Analysis Results:
-----------------------
Detected Function Names:
- greet
- __init__
- display
- calculate_sum
Node Type Counts:
- Arg: 4
- Arguments: 4
- Assign: 1
- Attribute: 1
- BinOp: 1
- Call: 1
- ClassDef: 1
- Constant: 3
- Expr: 1
- FString: 1
- FormattedValue: 1
- FunctionDef: 4
- Load: 8
- Module: 1
- Name: 12
- Return: 2
- Store: 1
- With: 0
```
### Small Challenge π§
Can you modify the `NodeCounter` class to also find and list all `ClassDef` names present in the `code_snippet`? Hint: You'll need to look for `ast.ClassDef` nodes!
---
Here's your Telegram post for Day 32 on Metaclasses!
π Day 32 of our Python journey takes us deep into the magic! Today, we're unveiling **Metaclasses**! π§ββοΈ Forget just creating objects; metaclasses let you **architect how classes themselves are created and behave**. Think frameworks, ORMs, and advanced library design. Ready to bend Python's reality? Let's dive into dynamic class creation beyond `type()`! β¨
```python
class ClassRegistryMeta(type):
# A dictionary to store all classes created by this metaclass
_registry = {}
def __new__(mcs, name, bases, attrs):
print(f"Metaclass: Preparing to create class '{name}'...")
# You can inspect/modify 'attrs' before the class is created
# For example, ensure a method exists or add new attributes
if '__str__' not in attrs:
attrs['__str__'] = lambda self: f"<Registered Object: {self.__class__.__name__}>"
# Create the class using the parent (type) __new__ method
new_class = super().__new__(mcs, name, bases, attrs)
# Add a custom class-level attribute
new_class._registered_name = name
# Register the new class in our metaclass's registry
mcs._registry[name] = new_class
print(f"Metaclass: Class '{name}' created and registered.")
return new_class
# Define classes using our custom metaclass
class MyService(metaclass=ClassRegistryMeta):
def __init__(self, id):
self.id = id
def identify(self):
print(f"{self._registered_name} instance with ID: {self.id}")
class AnotherComponent(metaclass=ClassRegistryMeta):
def __init__(self, data):
self.data = data
def show_data(self):
print(f"{self._registered_name} processing data: {self.data}")
print("n--- Class Definitions Complete ---")
# Accessing the registry via the metaclass itself
print(f"nAll registered classes: {list(ClassRegistryMeta._registry.keys())}")
# Instantiate registered classes using the registry
service_instance = ClassRegistryMeta._registry["MyService"](101)
service_instance.identify()
print(service_instance) # Uses the __str__ added by the metaclass
component_instance = ClassRegistryMeta._registry["AnotherComponent"]({"key": "value"})
component_instance.show_data()
print(component_instance)
```
Output example:
```
Metaclass: Preparing to create class 'MyService'...
Metaclass: Class 'MyService' created and registered.
Metaclass: Preparing to create class 'AnotherComponent'...
Metaclass: Class 'AnotherComponent' created and registered.
--- Class Definitions Complete ---
All registered classes: ['MyService', 'AnotherComponent']
MyService instance with ID: 101
<Registered Object: MyService>
AnotherComponent processing data: {'key': 'value'}
<Registered Object: AnotherComponent>
```
**Challenge:**
Modify the `ClassRegistryMeta` to automatically add a `VERSION = "1.0"` attribute to any class it creates, but only if the class does *not* already define a `VERSION` attribute.
---
π Day 32 of our Python journey takes us deep into the magic! Today, we're unveiling **Metaclasses**! π§ββοΈ Forget just creating objects; metaclasses let you **architect how classes themselves are created and behave**. Think frameworks, ORMs, and advanced library design. Ready to bend Python's reality? Let's dive into dynamic class creation beyond `type()`! β¨
```python
class ClassRegistryMeta(type):
# A dictionary to store all classes created by this metaclass
_registry = {}
def __new__(mcs, name, bases, attrs):
print(f"Metaclass: Preparing to create class '{name}'...")
# You can inspect/modify 'attrs' before the class is created
# For example, ensure a method exists or add new attributes
if '__str__' not in attrs:
attrs['__str__'] = lambda self: f"<Registered Object: {self.__class__.__name__}>"
# Create the class using the parent (type) __new__ method
new_class = super().__new__(mcs, name, bases, attrs)
# Add a custom class-level attribute
new_class._registered_name = name
# Register the new class in our metaclass's registry
mcs._registry[name] = new_class
print(f"Metaclass: Class '{name}' created and registered.")
return new_class
# Define classes using our custom metaclass
class MyService(metaclass=ClassRegistryMeta):
def __init__(self, id):
self.id = id
def identify(self):
print(f"{self._registered_name} instance with ID: {self.id}")
class AnotherComponent(metaclass=ClassRegistryMeta):
def __init__(self, data):
self.data = data
def show_data(self):
print(f"{self._registered_name} processing data: {self.data}")
print("n--- Class Definitions Complete ---")
# Accessing the registry via the metaclass itself
print(f"nAll registered classes: {list(ClassRegistryMeta._registry.keys())}")
# Instantiate registered classes using the registry
service_instance = ClassRegistryMeta._registry["MyService"](101)
service_instance.identify()
print(service_instance) # Uses the __str__ added by the metaclass
component_instance = ClassRegistryMeta._registry["AnotherComponent"]({"key": "value"})
component_instance.show_data()
print(component_instance)
```
Output example:
```
Metaclass: Preparing to create class 'MyService'...
Metaclass: Class 'MyService' created and registered.
Metaclass: Preparing to create class 'AnotherComponent'...
Metaclass: Class 'AnotherComponent' created and registered.
--- Class Definitions Complete ---
All registered classes: ['MyService', 'AnotherComponent']
MyService instance with ID: 101
<Registered Object: MyService>
AnotherComponent processing data: {'key': 'value'}
<Registered Object: AnotherComponent>
```
**Challenge:**
Modify the `ClassRegistryMeta` to automatically add a `VERSION = "1.0"` attribute to any class it creates, but only if the class does *not* already define a `VERSION` attribute.
---
What is the primary role of a Python metaclass?
Anonymous Quiz
0%
A) To define instance methods and attributes for objects.
0%
B) To control the creation and behavior of *classes* themselves.
0%
C) To provide an interface for abstract base classes.
0%
D) To handle asynchronous operations and coroutines.
π Day 33: Prepare to go *way* beyond surface-level Python! Today, we're deconstructing the interpreter itself. Ever wondered what happens to your code before it runs? We're diving deep into Python's Abstract Syntax Trees (AST) and bytecode. Understanding these internals unlocks immense power for runtime code transformation, static analysis, and even building your own language tools! Get ready to manipulate Python at its very core. π§ββοΈβ¨
```python
import ast
import dis
import types
# 1. Original Python code string
original_code_str = """
def calculate_secret(x):
y = x * 2 + 5 # The constant '5' will be our target for modification
print(f"Original calculation for {x}: {y}")
return y
"""
# 2. Parse the code into an AST
original_ast = ast.parse(original_code_str)
# 3. Define a NodeTransformer to modify the AST
# We will change the constant '5' to '10' within the AST
class ConstantChanger(ast.NodeTransformer):
def visit_Constant(self, node):
if isinstance(node.value, int) and node.value == 5:
# Return a new Constant node with the modified value
return ast.Constant(value=10)
return node # Return the original node for others
# 4. Apply the transformation
modified_ast = ConstantChanger().visit(original_ast)
# Fix line numbers and other metadata in the modified AST
ast.fix_missing_locations(modified_ast)
# 5. Compile and execute both original and modified ASTs
# Compile the original AST and get the function
original_compiled_code = compile(original_ast, '<string>', 'exec')
original_namespace = {}
exec(original_compiled_code, original_namespace)
original_func = original_namespace['calculate_secret']
# Compile the modified AST and get the function
modified_compiled_code = compile(modified_ast, '<string>', 'exec')
modified_namespace = {}
exec(modified_compiled_code, modified_namespace)
modified_func = modified_namespace['calculate_secret']
print("--- Original Function Bytecode ---")
dis.dis(original_func)
print("n--- Modified Function Bytecode ---")
dis.dis(modified_func)
print("n--- Execution Comparison ---")
# Call both functions to observe the runtime transformation
original_func(10)
modified_func(10) # Observe the result of 'x * 2 + 10' instead of 'x * 2 + 5'
```
Output example:
(Note: `ast.dump` is verbose, so we'll highlight the key change and show `dis` and execution comparison.)
The `ast.dump` for the original AST would show `Constant(value=5)`.
The `ast.dump` for the modified AST would show `Constant(value=10)` in the corresponding place.
```
--- Original Function Bytecode ---
3 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (2)
4 BINARY_MULTIPLY
6 LOAD_CONST 2 (5) # Original constant value
8 BINARY_ADD
10 STORE_FAST 1 (y)
...
--- Modified Function Bytecode ---
3 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (2)
4 BINARY_MULTIPLY
6 LOAD_CONST 2 (10) # Modified constant value!
8 BINARY_ADD
10 STORE_FAST 1 (y)
...
--- Execution Comparison ---
Original calculation for 10: 25
Original calculation for 10: 30
```
Small challenge:
Can you modify the `ConstantChanger` class to not just change a constant, but to transform an arithmetic operator? For instance, change all `ast.Add` operations to `ast.Mult` operations within a specific function! π
---
```python
import ast
import dis
import types
# 1. Original Python code string
original_code_str = """
def calculate_secret(x):
y = x * 2 + 5 # The constant '5' will be our target for modification
print(f"Original calculation for {x}: {y}")
return y
"""
# 2. Parse the code into an AST
original_ast = ast.parse(original_code_str)
# 3. Define a NodeTransformer to modify the AST
# We will change the constant '5' to '10' within the AST
class ConstantChanger(ast.NodeTransformer):
def visit_Constant(self, node):
if isinstance(node.value, int) and node.value == 5:
# Return a new Constant node with the modified value
return ast.Constant(value=10)
return node # Return the original node for others
# 4. Apply the transformation
modified_ast = ConstantChanger().visit(original_ast)
# Fix line numbers and other metadata in the modified AST
ast.fix_missing_locations(modified_ast)
# 5. Compile and execute both original and modified ASTs
# Compile the original AST and get the function
original_compiled_code = compile(original_ast, '<string>', 'exec')
original_namespace = {}
exec(original_compiled_code, original_namespace)
original_func = original_namespace['calculate_secret']
# Compile the modified AST and get the function
modified_compiled_code = compile(modified_ast, '<string>', 'exec')
modified_namespace = {}
exec(modified_compiled_code, modified_namespace)
modified_func = modified_namespace['calculate_secret']
print("--- Original Function Bytecode ---")
dis.dis(original_func)
print("n--- Modified Function Bytecode ---")
dis.dis(modified_func)
print("n--- Execution Comparison ---")
# Call both functions to observe the runtime transformation
original_func(10)
modified_func(10) # Observe the result of 'x * 2 + 10' instead of 'x * 2 + 5'
```
Output example:
(Note: `ast.dump` is verbose, so we'll highlight the key change and show `dis` and execution comparison.)
The `ast.dump` for the original AST would show `Constant(value=5)`.
The `ast.dump` for the modified AST would show `Constant(value=10)` in the corresponding place.
```
--- Original Function Bytecode ---
3 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (2)
4 BINARY_MULTIPLY
6 LOAD_CONST 2 (5) # Original constant value
8 BINARY_ADD
10 STORE_FAST 1 (y)
...
--- Modified Function Bytecode ---
3 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (2)
4 BINARY_MULTIPLY
6 LOAD_CONST 2 (10) # Modified constant value!
8 BINARY_ADD
10 STORE_FAST 1 (y)
...
--- Execution Comparison ---
Original calculation for 10: 25
Original calculation for 10: 30
```
Small challenge:
Can you modify the `ConstantChanger` class to not just change a constant, but to transform an arithmetic operator? For instance, change all `ast.Add` operations to `ast.Mult` operations within a specific function! π
---
Which Python module is primarily used for programmatically inspecting, traversing, and modifying the Abstract Syntax Tree (AST) of Python code?
Anonymous Quiz
0%
A. `sys`
0%
B. `inspect`
0%
C. `ast`
0%
D. `dis`
π» Day 34: Unmasking the "Ghost in the Machine"! Ever wondered how to manage memory efficiently in Python, especially with caches or large data structures? Today, we dive into **Weak References** and **Ephemeral Objects**! π‘ Discover how to create references to objects without preventing them from being garbage collected, crucial for preventing memory leaks and building robust, memory-sensitive applications. Let's make our Python programs lighter and smarter! π§ β¨
```python
import weakref
import gc # Import gc to manually trigger collection for demonstration
class MyEphemeralObject:
def __init__(self, name):
self.name = name
print(f"β¨ Object '{self.name}' created.")
def __del__(self):
print(f"π Object '{self.name}' garbage collected.")
print("--- Scenario 1: Object collected when only weak references exist ---")
# 1. Create a strong reference
strong_ref = MyEphemeralObject("Ephemeral A")
# 2. Create a weak reference to the same object
# This reference will not prevent the object from being garbage collected
weak_ref = weakref.ref(strong_ref)
print(f"Weak reference currently points to: {weak_ref()}")
# 3. Delete the strong reference. Now, only the weak_ref points to the object.
print("nπ₯ Deleting the strong reference 'strong_ref'...")
del strong_ref
# Manually trigger garbage collection (often happens automatically, but good for demo)
gc.collect()
print("Attempting to access object via weak reference after strong reference deletion:")
retrieved_object = weak_ref()
if retrieved_object is None:
print("β Object has been garbage collected!")
else:
print(f"β Object still exists: {retrieved_object.name}")
print("n--- Scenario 2: Object NOT collected if a strong reference persists ---")
strong_ref_2 = MyEphemeralObject("Persistent B")
weak_ref_2 = weakref.ref(strong_ref_2)
print(f"Weak reference 2 points to: {weak_ref_2()}")
# We don't delete strong_ref_2 yet.
print("Strong reference 'strong_ref_2' still exists. Object should NOT be collected.")
gc.collect() # Trigger GC, but object is still strongly referenced
retrieved_object_2 = weak_ref_2()
if retrieved_object_2 is None:
print("β Object unexpectedly collected!")
else:
print(f"β Object still exists: {retrieved_object_2.name}")
# Now, let's delete the remaining strong reference for Persistent B
print("nπ₯ Deleting the strong reference 'strong_ref_2' for Persistent B...")
del strong_ref_2
gc.collect() # Trigger GC again
```
Output example:
```
--- Scenario 1: Object collected when only weak references exist ---
β¨ Object 'Ephemeral A' created.
Weak reference currently points to: <__main__.MyEphemeralObject object at 0x...>
π₯ Deleting the strong reference 'strong_ref'...
π Object 'Ephemeral A' garbage collected.
Attempting to access object via weak reference after strong reference deletion:
β Object has been garbage collected!
--- Scenario 2: Object NOT collected if a strong reference persists ---
β¨ Object 'Persistent B' created.
Weak reference 2 points to: <__main__.MyEphemeralObject object at 0x...>
Strong reference 'strong_ref_2' still exists. Object should NOT be collected.
β Object still exists: Persistent B
π₯ Deleting the strong reference 'strong_ref_2' for Persistent B...
π Object 'Persistent B' garbage collected.
```
π€ **Your Challenge:** Think of a common Python use case, like a large cache, a GUI framework, or a network connection pool, where using weak references would be absolutely critical to prevent memory leaks or excessive memory consumption. How would you apply them? Share your ideas! π
```python
import weakref
import gc # Import gc to manually trigger collection for demonstration
class MyEphemeralObject:
def __init__(self, name):
self.name = name
print(f"β¨ Object '{self.name}' created.")
def __del__(self):
print(f"π Object '{self.name}' garbage collected.")
print("--- Scenario 1: Object collected when only weak references exist ---")
# 1. Create a strong reference
strong_ref = MyEphemeralObject("Ephemeral A")
# 2. Create a weak reference to the same object
# This reference will not prevent the object from being garbage collected
weak_ref = weakref.ref(strong_ref)
print(f"Weak reference currently points to: {weak_ref()}")
# 3. Delete the strong reference. Now, only the weak_ref points to the object.
print("nπ₯ Deleting the strong reference 'strong_ref'...")
del strong_ref
# Manually trigger garbage collection (often happens automatically, but good for demo)
gc.collect()
print("Attempting to access object via weak reference after strong reference deletion:")
retrieved_object = weak_ref()
if retrieved_object is None:
print("β Object has been garbage collected!")
else:
print(f"β Object still exists: {retrieved_object.name}")
print("n--- Scenario 2: Object NOT collected if a strong reference persists ---")
strong_ref_2 = MyEphemeralObject("Persistent B")
weak_ref_2 = weakref.ref(strong_ref_2)
print(f"Weak reference 2 points to: {weak_ref_2()}")
# We don't delete strong_ref_2 yet.
print("Strong reference 'strong_ref_2' still exists. Object should NOT be collected.")
gc.collect() # Trigger GC, but object is still strongly referenced
retrieved_object_2 = weak_ref_2()
if retrieved_object_2 is None:
print("β Object unexpectedly collected!")
else:
print(f"β Object still exists: {retrieved_object_2.name}")
# Now, let's delete the remaining strong reference for Persistent B
print("nπ₯ Deleting the strong reference 'strong_ref_2' for Persistent B...")
del strong_ref_2
gc.collect() # Trigger GC again
```
Output example:
```
--- Scenario 1: Object collected when only weak references exist ---
β¨ Object 'Ephemeral A' created.
Weak reference currently points to: <__main__.MyEphemeralObject object at 0x...>
π₯ Deleting the strong reference 'strong_ref'...
π Object 'Ephemeral A' garbage collected.
Attempting to access object via weak reference after strong reference deletion:
β Object has been garbage collected!
--- Scenario 2: Object NOT collected if a strong reference persists ---
β¨ Object 'Persistent B' created.
Weak reference 2 points to: <__main__.MyEphemeralObject object at 0x...>
Strong reference 'strong_ref_2' still exists. Object should NOT be collected.
β Object still exists: Persistent B
π₯ Deleting the strong reference 'strong_ref_2' for Persistent B...
π Object 'Persistent B' garbage collected.
```
π€ **Your Challenge:** Think of a common Python use case, like a large cache, a GUI framework, or a network connection pool, where using weak references would be absolutely critical to prevent memory leaks or excessive memory consumption. How would you apply them? Share your ideas! π
What is the primary purpose of a weak reference in Python?
Anonymous Quiz
0%
A) To create a new copy of an object without increasing memory usage.
0%
B) To allow an object to be garbage collected even if weak references to it still exist.
0%
C) To explicitly prevent an object from being garbage collected by the Python runtime.
0%
D) To manage memory allocation and deallocation manually, similar to C++ pointers.