Python by Structure: Return Value Transformations with Decorators
Source: Dev.to
The Problem: Repetitive Output Transformations
def get_greeting(name):
return f"hello, {name}".upper()
def get_status():
return "system online".upper()
def get_error_message(code):
return f"error code: {code}".upper()
Using a Decorator to Transform Return Values
def uppercase_result(func):
"""Decorator to convert a function's string result to uppercase."""
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if isinstance(result, str):
return result.upper()
return result
return wrapper
@uppercase_result
def get_greeting(name):
"""Returns a personalized greeting."""
return f"hello, {name}"
@uppercase_result
def get_status():
"""Returns the system status."""
return "system online"
@uppercase_result
def get_error_message(code):
"""Returns an error message."""
return f"error code: {code}"
# Usage
print(get_greeting("Alice"))
print(get_status())
print(get_error_message(404))
Output
HELLO, ALICE
SYSTEM ONLINE
ERROR CODE: 404
Understanding Return Value Interception
The decorator works by:
- Calling the original function.
- Capturing its result.
- Checking if the result is a string.
- Applying
.upper()only when appropriate. - Returning the (possibly transformed) result.
Type‑Safe Transformations
The type check prevents errors when a decorated function returns a non‑string value:
@uppercase_result
def get_error_code():
"""Returns a number, which should not be affected."""
return 404
print(get_error_code()) # Outputs: 404
When to Use Return‑Value Decorators
Use return‑value transformation decorators when you need to:
- Apply consistent formatting to function outputs (uppercase, lowercase, title case)
- Round or format numeric results consistently
- Sanitize or escape output for security
- Convert between data formats (dict to JSON, object to string)
- Add metadata or wrap results in a standard structure
Example: Rounding Numeric Results
def round_result(decimals=2):
"""Decorator to round numeric results to specified decimals."""
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if isinstance(result, (int, float)):
return round(result, decimals)
return result
return wrapper
return decorator
@round_result(decimals=2)
def calculate_average(numbers):
"""Calculates the average of a list of numbers."""
return sum(numbers) / len(numbers)
print(calculate_average([1.234, 2.567, 3.891])) # Outputs: 2.56
Keeping Functions Clean
By separating concerns, functions remain focused on their core logic while decorators handle presentation or formatting:
@uppercase_result
def get_greeting(name):
return f"hello, {name}"
Changing the transformation only requires updating the decorator, not every individual function. This promotes cleaner, more maintainable code.