🚀 Day 64: Ever wondered how Python truly works under the hood? Today, we're diving deep into the very blueprint of your code: the Abstract Syntax Tree (AST)! 🤯 Learn to unmask, transform, and even generate Python code at runtime by directly manipulating its AST. This isn't just theory; it's a powerful technique for advanced metaprogramming, dynamic code generation, and building highly adaptable systems. Prepare to truly understand your code's structure! ✨
In this example, we'll parse a simple Python function, modify its AST to inject a logging `print` statement at the beginning of the function, and then execute the transformed code. See how we can alter code's behavior without touching the source string directly!
```python
import ast
import inspect
# Original Python code as a string
original_code = """
def calculate_sum(a, b):
# This is the original function body
result = a + b
return result
# This will print the result using the original function definition
print(f"Original sum result: {calculate_sum(5, 3)}")
"""
class FunctionLoggerTransformer(ast.NodeTransformer):
"""
A custom AST transformer that adds a print statement to the beginning
of any function it processes, logging its execution.
"""
def visit_FunctionDef(self, node):
# Create a new print statement node: print(f"--- Executing function: '{node.name}' ---")
# We embed the function's name (from AST transformation time) directly into the string.
log_message = ast.Constant(value=f"--- Executing function: '{node.name}' ---")
print_statement = ast.Expr(
value=ast.Call(
func=ast.Name(id='print', ctx=ast.Load()), # Call to the 'print' built-in
args=[log_message], # Argument is our logging message
keywords=[]
)
)
# Prepend the new print statement to the function's body
node.body.insert(0, print_statement)
# Important: Call generic_visit to ensure child nodes are also visited and potentially transformed
return self.generic_visit(node)
print("Original code before transformation:n" + original_code)
# 1. Parse the original code into an AST
tree = ast.parse(original_code)
# 2. Transform the AST using our custom transformer
transformer = FunctionLoggerTransformer()
new_tree = transformer.visit(tree)
# 3. Fix missing line numbers and column offsets in the new tree.
# This is crucial for compilation to work correctly and prevent errors.
ast.fix_missing_locations(new_tree)
# 4. Compile the modified AST back into executable code
# filename='<string>' is standard for in-memory code
# mode='exec' for a sequence of statements or a module
compiled_code = compile(new_tree, filename='<string>', mode='exec')
print("n--- Executing Transformed Code ---")
# 5. Execute the compiled code in a new scope
# This will run the `print` statement that calls `calculate_sum` from the original_code,
# but now `calculate_sum` has our injected logger.
exec_scope = {} # Define a scope for execution to catch definitions if needed
exec(compiled_code, exec_scope)
```
Output example:
```
Original code before transformation:
def calculate_sum(a, b):
# This is the original function body
result = a + b
return result
# This will print the result using the original function definition
print(f"Original sum result: {calculate_sum(5, 3)}")
--- Executing Transformed Code ---
--- Executing function: 'calculate_sum' ---
Original sum result: 8
```
💡 **Your Challenge:** Modify the `FunctionLoggerTransformer` to not just log the function execution, but also log the *arguments* passed to the function! Hint: You'll need to inspect `node.args` within `visit_FunctionDef` and create an AST node for a dictionary or a formatted string that prints argument names and values. Good luck!
---
📚 Quiz Question:
In this example, we'll parse a simple Python function, modify its AST to inject a logging `print` statement at the beginning of the function, and then execute the transformed code. See how we can alter code's behavior without touching the source string directly!
```python
import ast
import inspect
# Original Python code as a string
original_code = """
def calculate_sum(a, b):
# This is the original function body
result = a + b
return result
# This will print the result using the original function definition
print(f"Original sum result: {calculate_sum(5, 3)}")
"""
class FunctionLoggerTransformer(ast.NodeTransformer):
"""
A custom AST transformer that adds a print statement to the beginning
of any function it processes, logging its execution.
"""
def visit_FunctionDef(self, node):
# Create a new print statement node: print(f"--- Executing function: '{node.name}' ---")
# We embed the function's name (from AST transformation time) directly into the string.
log_message = ast.Constant(value=f"--- Executing function: '{node.name}' ---")
print_statement = ast.Expr(
value=ast.Call(
func=ast.Name(id='print', ctx=ast.Load()), # Call to the 'print' built-in
args=[log_message], # Argument is our logging message
keywords=[]
)
)
# Prepend the new print statement to the function's body
node.body.insert(0, print_statement)
# Important: Call generic_visit to ensure child nodes are also visited and potentially transformed
return self.generic_visit(node)
print("Original code before transformation:n" + original_code)
# 1. Parse the original code into an AST
tree = ast.parse(original_code)
# 2. Transform the AST using our custom transformer
transformer = FunctionLoggerTransformer()
new_tree = transformer.visit(tree)
# 3. Fix missing line numbers and column offsets in the new tree.
# This is crucial for compilation to work correctly and prevent errors.
ast.fix_missing_locations(new_tree)
# 4. Compile the modified AST back into executable code
# filename='<string>' is standard for in-memory code
# mode='exec' for a sequence of statements or a module
compiled_code = compile(new_tree, filename='<string>', mode='exec')
print("n--- Executing Transformed Code ---")
# 5. Execute the compiled code in a new scope
# This will run the `print` statement that calls `calculate_sum` from the original_code,
# but now `calculate_sum` has our injected logger.
exec_scope = {} # Define a scope for execution to catch definitions if needed
exec(compiled_code, exec_scope)
```
Output example:
```
Original code before transformation:
def calculate_sum(a, b):
# This is the original function body
result = a + b
return result
# This will print the result using the original function definition
print(f"Original sum result: {calculate_sum(5, 3)}")
--- Executing Transformed Code ---
--- Executing function: 'calculate_sum' ---
Original sum result: 8
```
💡 **Your Challenge:** Modify the `FunctionLoggerTransformer` to not just log the function execution, but also log the *arguments* passed to the function! Hint: You'll need to inspect `node.args` within `visit_FunctionDef` and create an AST node for a dictionary or a formatted string that prints argument names and values. Good luck!
---
📚 Quiz Question:
Which Python module is primarily used for parsing Python code into an Abstract Syntax Tree (AST) for programmatic manipulation?
Anonymous Quiz
0%
A. `parser`
0%
B. `ast`
0%
C. `tokenize`
0%
D. `compiler`
🚀 Day 65: Ever wanted to bend Python's rules? 🤯 Today, we dive into the Abstract Syntax Tree (AST)! No, it's not just for linters. We'll learn how to programmatically *rewrite* your code before it even runs, opening doors to custom language features, powerful static analysis, or even runtime optimizations. Get ready to transform your Python! 🛠️✨
Let's craft a simple AST transformer to automatically log when any function is entered.
```python
import ast
import sys
from io import StringIO
# Original code to transform
original_code = """
def greet(name):
print(f"Hello, {name}!")
return f"Greeting for {name}."
def calculate_sum(a, b):
result = a + b
print(f"Sum is {result}")
return result
print("Before AST transformation")
greet("Alice")
calculate_sum(10, 20)
"""
class FunctionCallLogger(ast.NodeTransformer):
"""
AST transformer to inject a print statement at the beginning
of every function definition, logging function entry.
"""
def visit_FunctionDef(self, node):
# Create a print statement node to log function entry
log_message = ast.Constant(value=f"Entering function: {node.name}()")
print_call = ast.Expr(
value=ast.Call(
func=ast.Name(id='print', ctx=ast.Load()),
args=[log_message],
keywords=[]
)
)
# Insert the print statement at the beginning of the function's body
node.body.insert(0, print_call)
# Ensure we visit child nodes (e.g., nested functions)
return self.generic_visit(node)
# Parse the original code into an AST
tree = ast.parse(original_code)
# Apply the transformer to modify the AST
transformer = FunctionCallLogger()
new_tree = transformer.visit(tree)
# --- Compile and execute the transformed code ---
# Redirect stdout to capture the output for demonstration
old_stdout = sys.stdout
redirected_output = StringIO()
sys.stdout = redirected_output
try:
print("--- Rewritten Code Preview (Python 3.9+) ---")
# ast.unparse is great for viewing the transformed code,
# but only available in Python 3.9 and later.
try:
print(ast.unparse(new_tree))
except AttributeError:
print("# ast.unparse not available. The transformed code below will be executed.")
print("n--- Executing Transformed Code ---")
# Compile the modified AST back into executable code and run it
exec(compile(new_tree, '<string>', 'exec'))
except Exception as e:
print(f"Error during execution: {e}")
finally:
sys.stdout = old_stdout # Restore stdout
print(redirected_output.getvalue())
```
Output example:
```
--- Rewritten Code Preview (Python 3.9+) ---
def greet(name):
print('Entering function: greet()')
print(f"Hello, {name}!")
return f"Greeting for {name}."
def calculate_sum(a, b):
print('Entering function: calculate_sum()')
result = a + b
print(f"Sum is {result}")
return result
print('Before AST transformation')
greet('Alice')
calculate_sum(10, 20)
--- Executing Transformed Code ---
Before AST transformation
Entering function: greet()
Hello, Alice!
Entering function: calculate_sum()
Sum is 30
```
**Challenge:** Modify the `FunctionCallLogger` transformer to also inject a `print` statement at the *end* of each function, showing an "Exiting function: {func_name}()" message! 🔚
---
Let's craft a simple AST transformer to automatically log when any function is entered.
```python
import ast
import sys
from io import StringIO
# Original code to transform
original_code = """
def greet(name):
print(f"Hello, {name}!")
return f"Greeting for {name}."
def calculate_sum(a, b):
result = a + b
print(f"Sum is {result}")
return result
print("Before AST transformation")
greet("Alice")
calculate_sum(10, 20)
"""
class FunctionCallLogger(ast.NodeTransformer):
"""
AST transformer to inject a print statement at the beginning
of every function definition, logging function entry.
"""
def visit_FunctionDef(self, node):
# Create a print statement node to log function entry
log_message = ast.Constant(value=f"Entering function: {node.name}()")
print_call = ast.Expr(
value=ast.Call(
func=ast.Name(id='print', ctx=ast.Load()),
args=[log_message],
keywords=[]
)
)
# Insert the print statement at the beginning of the function's body
node.body.insert(0, print_call)
# Ensure we visit child nodes (e.g., nested functions)
return self.generic_visit(node)
# Parse the original code into an AST
tree = ast.parse(original_code)
# Apply the transformer to modify the AST
transformer = FunctionCallLogger()
new_tree = transformer.visit(tree)
# --- Compile and execute the transformed code ---
# Redirect stdout to capture the output for demonstration
old_stdout = sys.stdout
redirected_output = StringIO()
sys.stdout = redirected_output
try:
print("--- Rewritten Code Preview (Python 3.9+) ---")
# ast.unparse is great for viewing the transformed code,
# but only available in Python 3.9 and later.
try:
print(ast.unparse(new_tree))
except AttributeError:
print("# ast.unparse not available. The transformed code below will be executed.")
print("n--- Executing Transformed Code ---")
# Compile the modified AST back into executable code and run it
exec(compile(new_tree, '<string>', 'exec'))
except Exception as e:
print(f"Error during execution: {e}")
finally:
sys.stdout = old_stdout # Restore stdout
print(redirected_output.getvalue())
```
Output example:
```
--- Rewritten Code Preview (Python 3.9+) ---
def greet(name):
print('Entering function: greet()')
print(f"Hello, {name}!")
return f"Greeting for {name}."
def calculate_sum(a, b):
print('Entering function: calculate_sum()')
result = a + b
print(f"Sum is {result}")
return result
print('Before AST transformation')
greet('Alice')
calculate_sum(10, 20)
--- Executing Transformed Code ---
Before AST transformation
Entering function: greet()
Hello, Alice!
Entering function: calculate_sum()
Sum is 30
```
**Challenge:** Modify the `FunctionCallLogger` transformer to also inject a `print` statement at the *end* of each function, showing an "Exiting function: {func_name}()" message! 🔚
---
Which `ast` module class is specifically designed for visiting and *modifying* nodes within an Abstract Syntax Tree?
Anonymous Quiz
0%
ast.NodeTransformer
0%
ast.NodeVisitor
0%
ast.parse
0%
ast.dump
Which of the following methods is primarily responsible for *retrieving* an attribute's value when a descriptor is accessed on an instance (e.g., `obj.attribute`)?
Anonymous Quiz
0%
__set__
0%
__get__
0%
__call__
0%
__set_name__
Here's your Telegram post for the Python learning series!
---
🚀 Ready to truly command your Python types? Dive into the "Deep Magic" of how classes are born! ✨ This week, we're unraveling Metaclasses and the powerful `__init_subclass__` hook. Learn to architect runtime behavior, enforce design patterns, and inject custom logic *before* your classes even fully exist. Elevate your object-oriented design from consumer to creator! 🛠️
Let's see how they let us define a custom class creation pipeline:
```python
import datetime
# --- Part 1: Using __init_subclass__ for a simple hook ---
# This method is called on the *base class* when a new *subclass* is defined.
class BaseModel:
_registry = [] # A simple registry for demonstration
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
# Enforce a docstring for all subclasses
if not cls.__doc__ or not cls.__doc__.strip():
raise TypeError(f"Class '{cls.__name__}' must have a docstring!")
# Add a creation timestamp at class creation time
cls._creation_timestamp = datetime.datetime.now()
BaseModel._registry.append(cls)
print(f"[{cls.__name__}] registered and timestamped at {cls._creation_timestamp.strftime('%H:%M:%S')}")
class MyData(BaseModel):
"""Represents some user data."""
def __init__(self, value):
self.value = value
class AnotherData(BaseModel):
"""More data here."""
pass
# # Uncomment the class below to see the TypeError in action!
# class NoDocstring(BaseModel):
# pass
# --- Part 2: A simple Metaclass example ---
# A metaclass controls how classes (its instances) are created.
class EnforcingMeta(type):
def __new__(mcs, name, bases, namespace):
if 'ID_PREFIX' not in namespace:
raise AttributeError(f"Class '{name}' using EnforcingMeta must define 'ID_PREFIX'.")
# Ensure ID_PREFIX starts with 'APP_'
if not namespace['ID_PREFIX'].startswith('APP_'):
raise ValueError(f"ID_PREFIX for '{name}' must start with 'APP_'.")
# Add a default method if not present
if 'get_full_id' not in namespace:
def default_get_full_id(self):
return f"{self.ID_PREFIX}-{self.item_id}"
namespace['get_full_id'] = default_get_full_id
return super().__new__(mcs, name, bases, namespace)
class MyItem(metaclass=EnforcingMeta):
ID_PREFIX = "APP_ITEM"
def __init__(self, item_id):
self.item_id = item_id
# Metaclasses are inherited, but specifying it explicitly works too.
class MyProduct(MyItem, metaclass=EnforcingMeta):
ID_PREFIX = "APP_PRODUCT"
def __init__(self, item_id, price):
super().__init__(item_id)
self.price = price
print("n--- Testing Instances ---")
data1 = MyData("Hello")
print(f"MyData created at: {MyData._creation_timestamp.strftime('%H:%M:%S')}")
item1 = MyItem(123)
print(f"Item ID: {item1.get_full_id()}")
product1 = MyProduct(456, 99.99)
print(f"Product ID: {product1.get_full_id()} | Price: {product1.price}")
print("n--- Registry Check ---")
for cls in BaseModel._registry:
print(f"- Registered: {cls.__name__}")
```
Output example:
```
[MyData] registered and timestamped at 14:02:45
[AnotherData] registered and timestamped at 14:02:45
--- Testing Instances ---
MyData created at: 14:02:45
Item ID: APP_ITEM-123
Product ID: APP_PRODUCT-456 | Price: 99.99
--- Registry Check ---
- Registered: MyData
- Registered: AnotherData
```
💡 **Challenge:** Modify the `EnforcingMeta` to also automatically add a *class method* `get_class_prefix()` that returns the `ID_PREFIX` defined by the class. How would you ensure this method correctly reflects the `ID_PREFIX` of the *subclass* if it overrides it? Share your solution in the comments! 👇
---
---
🚀 Ready to truly command your Python types? Dive into the "Deep Magic" of how classes are born! ✨ This week, we're unraveling Metaclasses and the powerful `__init_subclass__` hook. Learn to architect runtime behavior, enforce design patterns, and inject custom logic *before* your classes even fully exist. Elevate your object-oriented design from consumer to creator! 🛠️
Let's see how they let us define a custom class creation pipeline:
```python
import datetime
# --- Part 1: Using __init_subclass__ for a simple hook ---
# This method is called on the *base class* when a new *subclass* is defined.
class BaseModel:
_registry = [] # A simple registry for demonstration
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
# Enforce a docstring for all subclasses
if not cls.__doc__ or not cls.__doc__.strip():
raise TypeError(f"Class '{cls.__name__}' must have a docstring!")
# Add a creation timestamp at class creation time
cls._creation_timestamp = datetime.datetime.now()
BaseModel._registry.append(cls)
print(f"[{cls.__name__}] registered and timestamped at {cls._creation_timestamp.strftime('%H:%M:%S')}")
class MyData(BaseModel):
"""Represents some user data."""
def __init__(self, value):
self.value = value
class AnotherData(BaseModel):
"""More data here."""
pass
# # Uncomment the class below to see the TypeError in action!
# class NoDocstring(BaseModel):
# pass
# --- Part 2: A simple Metaclass example ---
# A metaclass controls how classes (its instances) are created.
class EnforcingMeta(type):
def __new__(mcs, name, bases, namespace):
if 'ID_PREFIX' not in namespace:
raise AttributeError(f"Class '{name}' using EnforcingMeta must define 'ID_PREFIX'.")
# Ensure ID_PREFIX starts with 'APP_'
if not namespace['ID_PREFIX'].startswith('APP_'):
raise ValueError(f"ID_PREFIX for '{name}' must start with 'APP_'.")
# Add a default method if not present
if 'get_full_id' not in namespace:
def default_get_full_id(self):
return f"{self.ID_PREFIX}-{self.item_id}"
namespace['get_full_id'] = default_get_full_id
return super().__new__(mcs, name, bases, namespace)
class MyItem(metaclass=EnforcingMeta):
ID_PREFIX = "APP_ITEM"
def __init__(self, item_id):
self.item_id = item_id
# Metaclasses are inherited, but specifying it explicitly works too.
class MyProduct(MyItem, metaclass=EnforcingMeta):
ID_PREFIX = "APP_PRODUCT"
def __init__(self, item_id, price):
super().__init__(item_id)
self.price = price
print("n--- Testing Instances ---")
data1 = MyData("Hello")
print(f"MyData created at: {MyData._creation_timestamp.strftime('%H:%M:%S')}")
item1 = MyItem(123)
print(f"Item ID: {item1.get_full_id()}")
product1 = MyProduct(456, 99.99)
print(f"Product ID: {product1.get_full_id()} | Price: {product1.price}")
print("n--- Registry Check ---")
for cls in BaseModel._registry:
print(f"- Registered: {cls.__name__}")
```
Output example:
```
[MyData] registered and timestamped at 14:02:45
[AnotherData] registered and timestamped at 14:02:45
--- Testing Instances ---
MyData created at: 14:02:45
Item ID: APP_ITEM-123
Product ID: APP_PRODUCT-456 | Price: 99.99
--- Registry Check ---
- Registered: MyData
- Registered: AnotherData
```
💡 **Challenge:** Modify the `EnforcingMeta` to also automatically add a *class method* `get_class_prefix()` that returns the `ID_PREFIX` defined by the class. How would you ensure this method correctly reflects the `ID_PREFIX` of the *subclass* if it overrides it? Share your solution in the comments! 👇
---
Which of the following statements about the `__init_subclass__` method is TRUE?
Anonymous Quiz
0%
It is a class method automatically called when an *instance* of the class is created.
0%
It is a special method called on the *base class* whenever one of its direct subclasses is defined.
0%
It allows a class to define its own custom instantiation logic for *objects*.
0%
It replaces the functionality of a metaclass entirely for all class creation scenarios.
Hey Pythonistas! 👋 It's Day 69, and we're diving deep into the very fabric of Python! 🚀 Ever wondered how frameworks define their own rules or how you could build a mini-language within Python? Today, we unlock the power of Metaclasses! 🏗️
Think of them as factories for classes – they let you define *how* classes are created, enforcing architectural patterns, injecting attributes, or even building Domain-Specific Languages (DSLs) directly. It's advanced, it's mind-bending, and incredibly powerful! ✨ Get ready to architect reality.
```python
class DomainMetaclass(type):
"""
A metaclass to enforce structure and add common behavior to domain objects.
Here, it ensures a 'key' attribute exists and creates a registry for instances.
"""
def __new__(mcs, name, bases, dct):
# 1. Enforce/Default an attribute: Ensure every class has a 'key'
if "key" not in dct:
dct["key"] = name.lower() # Default key if not provided
# 2. Add class-level attributes: A registry for elements of this type
dct["_registered_elements"] = []
# 3. Create the class using the modified dictionary
cls = super().__new__(mcs, name, bases, dct)
# 4. Add methods to the newly created class
def register_instance(instance):
"""Class method to register instances of this class."""
cls._registered_elements.append(instance)
print(f"✨ Registered '{instance}' with key '{instance.key}' in {cls.__name__}'s domain.")
# Attach the class method
cls.register_instance = classmethod(register_instance)
return cls
# --- Define Domain-Specific Classes using our Metaclass ---
class Command(metaclass=DomainMetaclass):
"""Represents a command in our DSL."""
# Metaclass ensures 'key' exists. We can override it:
key = "cmd"
def __init__(self, name, action):
self.name = name
self.action = action
# Use the metaclass-provided method to register this instance
self.register_instance(self)
def __repr__(self):
return f"<{self.__class__.__name__}: {self.name}>"
class Query(metaclass=DomainMetaclass):
"""Represents a query in our DSL."""
# Let the metaclass define the 'key' (will be 'query')
def __init__(self, statement):
self.statement = statement
self.register_instance(self)
def __repr__(self):
return f"<{self.__class__.__name__}: '{self.statement[:20]}...'>"
# --- Demonstrate the DSL-like behavior ---
print("--- Creating Domain Objects ---")
run_cmd = Command("RunScript", "Execute the main script logic")
stop_cmd = Command("StopService", "Gracefully halt a service")
get_users_query = Query("SELECT * FROM users WHERE status='active'")
count_products_query = Query("COUNT(*) FROM products")
print("n--- Inspecting Registered Elements ---")
print(f"Registered Commands (key='{Command.key}'): {Command._registered_elements}")
print(f"Registered Queries (key='{Query.key}'): {Query._registered_elements}")
```
Output example:
```
--- Creating Domain Objects ---
✨ Registered '<Command: RunScript>' with key 'cmd' in Command's domain.
✨ Registered '<Command: StopService>' with key 'cmd' in Command's domain.
✨ Registered '<Query: 'SELECT * FROM users...' with key 'query' in Query's domain.
✨ Registered '<Query: 'COUNT(*) FROM prod...' with key 'query' in Query's domain.
--- Inspecting Registered Elements ---
Registered Commands (key='cmd'): [<Command: RunScript>, <Command: StopService>]
Registered Queries (key='query'): [<Query: 'SELECT * FROM users...'>, <Query: 'COUNT(*) FROM prod...'>]
```
**Small challenge:**
Modify `DomainMetaclass` to automatically add a default `execute` method (printing "Executing...") to `Command` classes and a `fetch` method (printing "Fetching...") to `Query` classes. How would you ensure `Command` classes *must* have a `description` attribute (raising an error if missing) instead of just a default `key`?
---
Think of them as factories for classes – they let you define *how* classes are created, enforcing architectural patterns, injecting attributes, or even building Domain-Specific Languages (DSLs) directly. It's advanced, it's mind-bending, and incredibly powerful! ✨ Get ready to architect reality.
```python
class DomainMetaclass(type):
"""
A metaclass to enforce structure and add common behavior to domain objects.
Here, it ensures a 'key' attribute exists and creates a registry for instances.
"""
def __new__(mcs, name, bases, dct):
# 1. Enforce/Default an attribute: Ensure every class has a 'key'
if "key" not in dct:
dct["key"] = name.lower() # Default key if not provided
# 2. Add class-level attributes: A registry for elements of this type
dct["_registered_elements"] = []
# 3. Create the class using the modified dictionary
cls = super().__new__(mcs, name, bases, dct)
# 4. Add methods to the newly created class
def register_instance(instance):
"""Class method to register instances of this class."""
cls._registered_elements.append(instance)
print(f"✨ Registered '{instance}' with key '{instance.key}' in {cls.__name__}'s domain.")
# Attach the class method
cls.register_instance = classmethod(register_instance)
return cls
# --- Define Domain-Specific Classes using our Metaclass ---
class Command(metaclass=DomainMetaclass):
"""Represents a command in our DSL."""
# Metaclass ensures 'key' exists. We can override it:
key = "cmd"
def __init__(self, name, action):
self.name = name
self.action = action
# Use the metaclass-provided method to register this instance
self.register_instance(self)
def __repr__(self):
return f"<{self.__class__.__name__}: {self.name}>"
class Query(metaclass=DomainMetaclass):
"""Represents a query in our DSL."""
# Let the metaclass define the 'key' (will be 'query')
def __init__(self, statement):
self.statement = statement
self.register_instance(self)
def __repr__(self):
return f"<{self.__class__.__name__}: '{self.statement[:20]}...'>"
# --- Demonstrate the DSL-like behavior ---
print("--- Creating Domain Objects ---")
run_cmd = Command("RunScript", "Execute the main script logic")
stop_cmd = Command("StopService", "Gracefully halt a service")
get_users_query = Query("SELECT * FROM users WHERE status='active'")
count_products_query = Query("COUNT(*) FROM products")
print("n--- Inspecting Registered Elements ---")
print(f"Registered Commands (key='{Command.key}'): {Command._registered_elements}")
print(f"Registered Queries (key='{Query.key}'): {Query._registered_elements}")
```
Output example:
```
--- Creating Domain Objects ---
✨ Registered '<Command: RunScript>' with key 'cmd' in Command's domain.
✨ Registered '<Command: StopService>' with key 'cmd' in Command's domain.
✨ Registered '<Query: 'SELECT * FROM users...' with key 'query' in Query's domain.
✨ Registered '<Query: 'COUNT(*) FROM prod...' with key 'query' in Query's domain.
--- Inspecting Registered Elements ---
Registered Commands (key='cmd'): [<Command: RunScript>, <Command: StopService>]
Registered Queries (key='query'): [<Query: 'SELECT * FROM users...'>, <Query: 'COUNT(*) FROM prod...'>]
```
**Small challenge:**
Modify `DomainMetaclass` to automatically add a default `execute` method (printing "Executing...") to `Command` classes and a `fetch` method (printing "Fetching...") to `Query` classes. How would you ensure `Command` classes *must* have a `description` attribute (raising an error if missing) instead of just a default `key`?
---
What is the primary purpose of a Python metaclass?
Anonymous Quiz
0%
To define the base class for all built-in types.
0%
To control the creation of instances of a class.
0%
To define and modify how classes themselves are created.
0%
To handle asynchronous operations and coroutines.
Hey Pythonistas! 👋 Ready to unlock Python's deepest secrets?
For Day 70 of our learning series, we're diving into the mind-bending world of **Python as a Self-Modifying Organism: Advanced AST Introspection for Runtime Rewrites!** 🐍🔬 Ever wondered if your code could change its own logic while running? Today, we'll explore Abstract Syntax Trees (ASTs) not just for static analysis, but for actively inspecting, transforming, and even generating Python code at runtime. This isn't just theoretical; it's a powerful technique for building custom DSLs, optimizing on-the-fly, or creating advanced metaprogramming tools! Let's manipulate the very structure of our programs! ✨
```python
import ast
import inspect
# 1. Define a simple function whose AST we want to modify
def calculate_expression(a, b):
# This function performs multiplication, addition, and subtraction
return a * b + 5 - 2
# 2. Convert the function's source code into an AST
# We need the source of just the function body for easier transformation.
# For demonstration, we'll use a simplified string to avoid inspect issues with decorators/indentation.
code_to_transform = """
def transformed_expression(a, b):
result = a * b + 5 - 2
return result
"""
tree = ast.parse(code_to_transform)
# 3. Create a NodeTransformer to replace specific operations
class MultiplyToAddTransformer(ast.NodeTransformer):
"""
Transforms multiplication operations (ast.Mult) into addition operations (ast.Add).
Also changes subtraction (ast.Sub) to division (ast.Div).
"""
def visit_BinOp(self, node):
if isinstance(node.op, ast.Mult):
# Replace multiplication with addition
node.op = ast.Add()
elif isinstance(node.op, ast.Sub):
# Replace subtraction with division
node.op = ast.Div()
# Ensure we visit child nodes to apply transformations recursively
self.generic_visit(node)
return node
# Apply the transformer to our AST
transformed_tree = MultiplyToAddTransformer().visit(tree)
ast.fix_missing_locations(transformed_tree) # Best practice for transformed ASTs
# 4. Compile and execute the transformed AST
# 'compile' converts the AST back into a code object.
# 'exec' runs the code object within a specified namespace.
compiled_code = compile(transformed_tree, filename='<transformed_ast>', mode='exec')
# Create a new scope for our transformed function
transformed_scope = {}
exec(compiled_code, transformed_scope)
# Get the newly defined function from our transformed scope
modified_expression_func = transformed_scope['transformed_expression']
# 5. Demonstrate the original vs. transformed behavior
val_a, val_b = 10, 2
print(f"Original logic (conceptually for calculate_expression({val_a}, {val_b})):")
print(f" {val_a} * {val_b} + 5 - 2 = {calculate_expression(val_a, val_b)}")
print("nTransformed logic (after AST rewrite):")
print(f" (Original 'a * b' became 'a + b', '5 - 2' became '5 / 2')")
# Conceptually: 10 + 2 + 5 / 2 -> 12 + 2.5 = 14.5
print(f" transformed_expression({val_a}, {val_b}) -> {modified_expression_func(val_a, val_b)}")
```
**Output example:**
```
Original logic (conceptually for calculate_expression(10, 2)):
10 * 2 + 5 - 2 = 23
Transformed logic (after AST rewrite):
(Original 'a * b' became 'a + b', '5 - 2' became '5 / 2')
transformed_expression(10, 2) -> 14.5
```
**Small Challenge:**
Can you modify the `MultiplyToAddTransformer` to instead replace all integer literals (e.g., `5`, `2`) in the `transformed_expression` function with a different fixed value, like `0`? (Hint: You'll need to look for `ast.Constant` nodes and check their `value` type.) Good luck, AST alchemist! 🧙♂️
---
For Day 70 of our learning series, we're diving into the mind-bending world of **Python as a Self-Modifying Organism: Advanced AST Introspection for Runtime Rewrites!** 🐍🔬 Ever wondered if your code could change its own logic while running? Today, we'll explore Abstract Syntax Trees (ASTs) not just for static analysis, but for actively inspecting, transforming, and even generating Python code at runtime. This isn't just theoretical; it's a powerful technique for building custom DSLs, optimizing on-the-fly, or creating advanced metaprogramming tools! Let's manipulate the very structure of our programs! ✨
```python
import ast
import inspect
# 1. Define a simple function whose AST we want to modify
def calculate_expression(a, b):
# This function performs multiplication, addition, and subtraction
return a * b + 5 - 2
# 2. Convert the function's source code into an AST
# We need the source of just the function body for easier transformation.
# For demonstration, we'll use a simplified string to avoid inspect issues with decorators/indentation.
code_to_transform = """
def transformed_expression(a, b):
result = a * b + 5 - 2
return result
"""
tree = ast.parse(code_to_transform)
# 3. Create a NodeTransformer to replace specific operations
class MultiplyToAddTransformer(ast.NodeTransformer):
"""
Transforms multiplication operations (ast.Mult) into addition operations (ast.Add).
Also changes subtraction (ast.Sub) to division (ast.Div).
"""
def visit_BinOp(self, node):
if isinstance(node.op, ast.Mult):
# Replace multiplication with addition
node.op = ast.Add()
elif isinstance(node.op, ast.Sub):
# Replace subtraction with division
node.op = ast.Div()
# Ensure we visit child nodes to apply transformations recursively
self.generic_visit(node)
return node
# Apply the transformer to our AST
transformed_tree = MultiplyToAddTransformer().visit(tree)
ast.fix_missing_locations(transformed_tree) # Best practice for transformed ASTs
# 4. Compile and execute the transformed AST
# 'compile' converts the AST back into a code object.
# 'exec' runs the code object within a specified namespace.
compiled_code = compile(transformed_tree, filename='<transformed_ast>', mode='exec')
# Create a new scope for our transformed function
transformed_scope = {}
exec(compiled_code, transformed_scope)
# Get the newly defined function from our transformed scope
modified_expression_func = transformed_scope['transformed_expression']
# 5. Demonstrate the original vs. transformed behavior
val_a, val_b = 10, 2
print(f"Original logic (conceptually for calculate_expression({val_a}, {val_b})):")
print(f" {val_a} * {val_b} + 5 - 2 = {calculate_expression(val_a, val_b)}")
print("nTransformed logic (after AST rewrite):")
print(f" (Original 'a * b' became 'a + b', '5 - 2' became '5 / 2')")
# Conceptually: 10 + 2 + 5 / 2 -> 12 + 2.5 = 14.5
print(f" transformed_expression({val_a}, {val_b}) -> {modified_expression_func(val_a, val_b)}")
```
**Output example:**
```
Original logic (conceptually for calculate_expression(10, 2)):
10 * 2 + 5 - 2 = 23
Transformed logic (after AST rewrite):
(Original 'a * b' became 'a + b', '5 - 2' became '5 / 2')
transformed_expression(10, 2) -> 14.5
```
**Small Challenge:**
Can you modify the `MultiplyToAddTransformer` to instead replace all integer literals (e.g., `5`, `2`) in the `transformed_expression` function with a different fixed value, like `0`? (Hint: You'll need to look for `ast.Constant` nodes and check their `value` type.) Good luck, AST alchemist! 🧙♂️
---
What is the primary purpose of using `ast.NodeTransformer` when working with Python's Abstract Syntax Trees (ASTs)?
Anonymous Quiz
0%
To modify and rewrite the structure of an AST in place, enabling code transformation.
0%
To analyze the AST for syntax errors without making any changes.
0%
To convert an AST into a human-readable string representation.
0%
To execute Python code directly from an AST without compilation.
Hey everyone! We're starting again from Day 1 so everyone can build a strong foundation by learning everything from the basics this time.
🚀 **Day 1: Your Python Journey Begins!** 🐍
Welcome to the very first step of our Python learning series! Today, we're diving straight into the basics with the legendary "Hello, World!" program. It's the simplest way to confirm your setup works and celebrate your first line of code. Get ready to write, run, and feel the magic! ✨ Let's make some noise!
Here's your first piece of Python code:
```python
print("Hello, World!")
```
Output example:
```
Hello, World!
```
💪 **Your challenge:**
1. Change the text inside the `print()` function to say "Hello, Python Learners!"
2. Try printing your own name!
---
Welcome to the very first step of our Python learning series! Today, we're diving straight into the basics with the legendary "Hello, World!" program. It's the simplest way to confirm your setup works and celebrate your first line of code. Get ready to write, run, and feel the magic! ✨ Let's make some noise!
Here's your first piece of Python code:
```python
print("Hello, World!")
```
Output example:
```
Hello, World!
```
💪 **Your challenge:**
1. Change the text inside the `print()` function to say "Hello, Python Learners!"
2. Try printing your own name!
---
What is the primary purpose of the `print()` function in Python?
Anonymous Quiz
0%
To define a new variable
0%
To display output on the console
0%
To perform mathematical calculations
0%
To read input from the user
Learn Java daily with bite-sized lessons, practical code examples, output previews, and fun quizzes.
https://t.me/java_for_beginner
https://t.me/java_for_beginner
Telegram
Java for beginners
Learn Java daily with bite-sized lessons, practical code examples, output previews, and fun quizzes.
🚀 Welcome back, aspiring Pythonistas!
After a fantastic Day 1 intro, it's time to dive into the fundamental building blocks of any program: *Variables and Data Types*! 🧱
Think of variables as named containers for storing information. And data types? They tell Python what kind of information that is – numbers, text, true/false values, and more! Understanding these is crucial for writing dynamic and useful code. Let's get hands-on! 💻
---
```python
# Day 2: Variables and Data Types
# 1. Variable Assignment (creating containers for data)
student_name = "Alice Smith" # A string (text)
student_age = 20 # An integer (whole number)
gpa = 3.85 # A float (decimal number)
is_enrolled = True # A boolean (True/False)
# 2. Printing Values and their Data Types
print(f"Name: {student_name}, Type: {type(student_name)}")
print(f"Age: {student_age}, Type: {type(student_age)}")
print(f"GPA: {gpa}, Type: {type(gpa)}")
print(f"Is Enrolled: {is_enrolled}, Type: {type(is_enrolled)}")
# 3. Reassigning a variable (changing the content of a container)
student_age = 21 # Alice had a birthday!
print(f"nAlice's new age: {student_age}")
# You can also use single quotes for strings
course_name = 'Introduction to Python'
print(f"Course: {course_name}")
```
---
Output example:
```
Name: Alice Smith, Type: <class 'str'>
Age: 20, Type: <class 'int'>
GPA: 3.85, Type: <class 'float'>
Is Enrolled: True, Type: <class 'bool'>
Alice's new age: 21
Course: Introduction to Python
```
---
💡 **Your Challenge:**
Create three new variables:
1. `city` (a string for your hometown)
2. `population` (an integer)
3. `average_temp` (a float)
Then, print all three variables along with their respective data types using the `type()` function! Share your code or output in the comments! 👇
---
After a fantastic Day 1 intro, it's time to dive into the fundamental building blocks of any program: *Variables and Data Types*! 🧱
Think of variables as named containers for storing information. And data types? They tell Python what kind of information that is – numbers, text, true/false values, and more! Understanding these is crucial for writing dynamic and useful code. Let's get hands-on! 💻
---
```python
# Day 2: Variables and Data Types
# 1. Variable Assignment (creating containers for data)
student_name = "Alice Smith" # A string (text)
student_age = 20 # An integer (whole number)
gpa = 3.85 # A float (decimal number)
is_enrolled = True # A boolean (True/False)
# 2. Printing Values and their Data Types
print(f"Name: {student_name}, Type: {type(student_name)}")
print(f"Age: {student_age}, Type: {type(student_age)}")
print(f"GPA: {gpa}, Type: {type(gpa)}")
print(f"Is Enrolled: {is_enrolled}, Type: {type(is_enrolled)}")
# 3. Reassigning a variable (changing the content of a container)
student_age = 21 # Alice had a birthday!
print(f"nAlice's new age: {student_age}")
# You can also use single quotes for strings
course_name = 'Introduction to Python'
print(f"Course: {course_name}")
```
---
Output example:
```
Name: Alice Smith, Type: <class 'str'>
Age: 20, Type: <class 'int'>
GPA: 3.85, Type: <class 'float'>
Is Enrolled: True, Type: <class 'bool'>
Alice's new age: 21
Course: Introduction to Python
```
---
💡 **Your Challenge:**
Create three new variables:
1. `city` (a string for your hometown)
2. `population` (an integer)
3. `average_temp` (a float)
Then, print all three variables along with their respective data types using the `type()` function! Share your code or output in the comments! 👇
---
Which of the following is NOT a valid Python variable name?
Anonymous Quiz
0%
my_variable
0%
_private_data
0%
2nd_attempt
0%
user_count
Here's your Telegram post and quiz question!
---
🚀 Day 4: Time to make your Python programs smart! Today, we're mastering *Conditional Statements*: `if`, `elif`, and `else`. 🤖 These powerful tools let your code make decisions and execute different blocks of instructions based on specific conditions. No more rigid scripts – let's add some flexible logic! Get ready to control the flow of your programs like a pro! 👇
```python
# Day 4: Conditional Statements (if, elif, else)
# Get a score from the user
score = int(input("Enter your test score (0-100): "))
# Use if, elif, else to assign a grade based on the score
if score >= 90:
print("🎉 Excellent! You got an A!")
elif score >= 75:
print("👍 Good job! You got a B.")
elif score >= 50:
print("✅ You passed! Keep practicing.")
else:
print("🙁 Needs improvement. Don't give up!")
print("Program finished.")
```
Output example:
```
Enter your test score (0-100): 78
👍 Good job! You got a B.
Program finished.
```
✨ **Your Challenge:** Modify the code above! Add an `elif` condition to print 'Amazing!' if the score is exactly 100. How would you adjust the existing `if score >= 90` condition to fit this new rule? 🤔
---
---
🚀 Day 4: Time to make your Python programs smart! Today, we're mastering *Conditional Statements*: `if`, `elif`, and `else`. 🤖 These powerful tools let your code make decisions and execute different blocks of instructions based on specific conditions. No more rigid scripts – let's add some flexible logic! Get ready to control the flow of your programs like a pro! 👇
```python
# Day 4: Conditional Statements (if, elif, else)
# Get a score from the user
score = int(input("Enter your test score (0-100): "))
# Use if, elif, else to assign a grade based on the score
if score >= 90:
print("🎉 Excellent! You got an A!")
elif score >= 75:
print("👍 Good job! You got a B.")
elif score >= 50:
print("✅ You passed! Keep practicing.")
else:
print("🙁 Needs improvement. Don't give up!")
print("Program finished.")
```
Output example:
```
Enter your test score (0-100): 78
👍 Good job! You got a B.
Program finished.
```
✨ **Your Challenge:** Modify the code above! Add an `elif` condition to print 'Amazing!' if the score is exactly 100. How would you adjust the existing `if score >= 90` condition to fit this new rule? 🤔
---