JavaScript – Chapter 2: Development Environment
Learn what JavaScript is, where it runs, why it is important, and how beginners can start using it with simple examples and clear output.
Main reading content
Chapter 27: Decorators
A complete beginner-friendly guide to Python decorators, function wrapping, decorator arguments, metadata preservation, caching, timing, authentication, and practical applications.
Chapter 27 Topics
- 27.1 Introduction to Decorators
- 27.2 Functions as Objects
- 27.3 Nested Functions
- 27.4 Closures Review
- 27.5 Creating Simple Decorators
- 27.6 The
@Syntax - 27.7 Decorating Functions
- 27.8 Decorators with Arguments
- 27.9 Functions with Arguments
- 27.10 Preserving Function Metadata
- 27.11
functools.wraps - 27.12 Multiple Decorators
- 27.13 Decorator Order
- 27.14 Class Decorators
- 27.15 Decorating Methods
- 27.16 Caching Decorators
- 27.17 Timing Decorators
- 27.18 Authentication Decorators
- 27.19 Practical Decorator Applications
- 27.20 Chapter Practice Exercises
- 27.21 Chapter Mini Project
27.1 Introduction to Decorators
```A decorator is a Python tool that adds new behavior to a function or class without directly changing its original code. A decorator receives a function, creates a new function around it, and returns the new version. This process is often described as wrapping a function.
Decorators are useful when the same extra behavior must be applied to several functions. Common uses include logging, measuring execution time, checking permissions, validating input, caching results, counting calls, and handling errors. They reduce repeated code and keep programs organized.
Example
def simple_decorator(function):
def wrapper():
print("Before the function runs")
function()
print("After the function runs")
return wrapper
```
def greet():
print("Hello!")
decorated_greet = simple_decorator(greet)
decorated_greet()
```
Output
Before the function runs
```
Hello!
After the function runs
```
Output Explanation
The decorator receives the original greet() function. It returns a wrapper that prints a message before and after calling greet(). The original function still prints Hello!, but the decorator adds extra behavior around it.
27.2 Functions as Objects
```In Python, functions are objects. This means a function can be stored in a variable, passed to another function, placed inside a collection, or returned from another function. Decorators work because functions can be handled like other Python values.
Assigning a function to another variable does not call the function. The variable receives a reference to the function object. Parentheses are added only when you want to execute the function.
Example
def welcome():
print("Welcome to Python!")
```
# Store the function in another variable
message_function = welcome
# Display the function object
print(message_function)
# Call the function through the new variable
message_function()
```
Example Output
<function welcome at 0x000001AB12345670>
```
Welcome to Python!
```
Output Explanation
The first output shows that message_function refers to a function object. The memory address will differ on each computer. The second line appears when the function is called through the new variable.
27.3 Nested Functions
```A nested function is a function defined inside another function. The inner function normally exists only while the outer function is running. Nested functions are useful for organizing helper logic that should not be available everywhere in the program.
Decorators usually contain a nested wrapper function. The outer decorator receives the original function, while the inner wrapper controls what happens before, during, and after the original function runs.
Example
def outer_function():
print("The outer function started")
def inner_function():
print("The inner function is running")
inner_function()
print("The outer function finished")
```
outer_function()
```
Output
The outer function started
```
The inner function is running
The outer function finished
```
Output Explanation
The outer function begins and defines the inner function. The inner function runs only when it is called inside the outer function. After it finishes, execution returns to the outer function.
```27.4 Closures Review
```A closure is an inner function that remembers values from its enclosing function even after the outer function has finished. This happens because the inner function keeps access to variables that were available when it was created.
Closures are important for decorators because a wrapper must remember the original function. Even after the decorator function finishes, the wrapper still knows which original function it should call.
Example
def create_multiplier(multiplier):
def multiply(number):
return number * multiplier
return multiply
```
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5))
print(triple(5))
```
Output
10
```
15
```
Output Explanation
The double function remembers the value 2, while the triple function remembers the value 3. The outer function has already finished, but each returned function still remembers its multiplier.
27.5 Creating Simple Decorators
```A simple decorator usually contains three parts. First, the outer function receives the function being decorated. Second, a nested wrapper performs extra work and calls the original function. Third, the decorator returns the wrapper.
The result is a new function that includes both the original behavior and the additional decorator behavior. The original function name can then be replaced with the decorated version.
Example
def add_lines(function):
def wrapper():
print("--------------------")
function()
print("--------------------")
return wrapper
```
def show_message():
print("Python decorators are useful.")
show_message = add_lines(show_message)
show_message()
```
Output
--------------------
```
Python decorators are useful.
--------------------
```
Output Explanation
The decorator adds a line before and after the original message. The assignment replaces show_message with the wrapper returned by add_lines().
27.6 The @ Syntax
```
Python provides the @ syntax as a shorter and clearer way to apply decorators. The decorator name is placed directly above the function definition. Python then automatically passes the function to the decorator and replaces it with the returned wrapper.
Writing @add_lines above a function is equivalent to manually writing function = add_lines(function) after the function definition. The decorator syntax makes the relationship easier to see.
Example
def add_lines(function):
def wrapper():
print("====================")
function()
print("====================")
return wrapper
```
@add_lines
def display_title():
print("Python Course")
display_title()
```
Output
====================
```
Python Course
====================
```
Output Explanation
The @add_lines line applies the decorator to display_title(). Calling the function now runs the wrapper, which displays a line, calls the original function, and displays another line.
27.7 Decorating Functions
```A decorator can modify the behavior of many different functions. The same decorator may be applied to any compatible function, allowing programs to reuse one piece of logic without copying it into every function.
In the following example, the decorator announces when a function starts and finishes. The same decorator is applied to two separate functions, showing how decorators help remove repeated code.
Example
def announce(function):
def wrapper():
print(function.__name__, "is starting")
function()
print(function.__name__, "has finished")
return wrapper
```
@announce
def prepare_report():
print("Preparing the report...")
@announce
def send_email():
print("Sending the email...")
prepare_report()
print()
send_email()
```
Output
prepare_report is starting
```
Preparing the report...
prepare_report has finished
send_email is starting
Sending the email...
send_email has finished
```
Output Explanation
Both functions receive the same starting and finishing behavior. The decorator uses function.__name__ to display the original function's name.
27.8 Decorators with Arguments
```A decorator can receive its own arguments. This requires an additional outer function. The first function receives the decorator settings, the second receives the function being decorated, and the third wrapper runs when the decorated function is called.
Decorator arguments make decorators more flexible. For example, one decorator could repeat a function a selected number of times, display a custom label, require a specific role, or use a chosen logging level.
Example
def repeat(times):
def decorator(function):
def wrapper():
for _ in range(times):
function()
return wrapper
return decorator
```
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
```
Output
Hello!
```
Hello!
Hello!
```
Output Explanation
The value 3 is passed to repeat(). The decorator remembers this value and calls the original function three times whenever say_hello() is executed.
27.9 Functions with Arguments
```
A wrapper must accept the same arguments as the decorated function. Instead of listing every possible parameter, decorators commonly use *args and **kwargs. The *args parameter collects positional arguments, while **kwargs collects keyword arguments.
This approach allows one decorator to work with many functions, even when those functions have different parameters. The wrapper passes all received arguments to the original function.
Example
def show_arguments(function):
def wrapper(*args, **kwargs):
print("Positional arguments:", args)
print("Keyword arguments:", kwargs)
result = function(*args, **kwargs)
return result
return wrapper
```
@show_arguments
def introduce(name, age, city="Toronto"):
return f"{name} is {age} years old and lives in {city}."
message = introduce("Michael", 12, city="Richmond Hill")
print(message)
```
Output
Positional arguments: ('Michael', 12)
```
Keyword arguments: {'city': 'Richmond Hill'}
Michael is 12 years old and lives in Richmond Hill.
```
Output Explanation
The wrapper receives two positional arguments and one keyword argument. It displays them and then passes them to the original function. The function's return value is saved and returned by the wrapper.
```27.10 Preserving Function Metadata
```
Functions contain metadata such as their name, documentation string, module, and annotations. A basic decorator replaces the original function with a wrapper. As a result, information such as __name__ and __doc__ may describe the wrapper instead of the original function.
Losing metadata can make debugging, documentation, and development tools less accurate. Python provides functools.wraps to copy important information from the original function to the wrapper.
Example Without Metadata Preservation
def decorator(function):
def wrapper():
"""Documentation for the wrapper."""
return function()
return wrapper
```
@decorator
def greet():
"""Display a greeting."""
print("Hello!")
print("Function name:", greet.**name**)
print("Documentation:", greet.**doc**)
```
Output
Function name: wrapper
```
Documentation: Documentation for the wrapper.
```
Output Explanation
The decorated function now reports its name as wrapper. Its documentation also belongs to the wrapper. The original function metadata has been hidden by the decorator.
27.11 functools.wraps
```
The functools.wraps decorator copies metadata from an original function to its wrapper. It should normally be used whenever you create a function decorator. This keeps the decorated function's name, documentation, annotations, and other information accurate.
The @wraps(function) line is placed directly above the wrapper definition. It also creates a __wrapped__ attribute that points to the original function.
Example
from functools import wraps
```
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
print("Calling:", function.**name**)
return function(*args, **kwargs)
```
return wrapper
```
@decorator
def greet(name):
"""Display a personalized greeting."""
return f"Hello, {name}!"
print(greet("Sara"))
print("Function name:", greet.**name**)
print("Documentation:", greet.**doc**)
```
Output
Calling: greet
```
Hello, Sara!
Function name: greet
Documentation: Display a personalized greeting.
```
Output Explanation
The decorated function keeps the name greet and its original documentation string. The decorator still adds behavior, but the function's identity remains accurate.
27.12 Multiple Decorators
```A function can have more than one decorator. Each decorator adds its own behavior. Multiple decorators are written on separate lines above the function definition.
The decorator closest to the function is applied first. The decorator above it then wraps the result. This is similar to placing one wrapped package inside another wrapped package.
Example
from functools import wraps
```
def uppercase(function):
@wraps(function)
def wrapper(*args, **kwargs):
result = function(*args, **kwargs)
return result.upper()
```
return wrapper
```
def add_stars(function):
@wraps(function)
def wrapper(*args, **kwargs):
result = function(*args, **kwargs)
return "*** " + result + " ***"
```
return wrapper
```
@add_stars
@uppercase
def greeting(name):
return f"Hello, {name}"
print(greeting("Michael"))
```
Output
*** HELLO, MICHAEL ***
Output Explanation
The uppercase decorator is closest to the function, so it converts the greeting to uppercase first. The add_stars decorator then adds stars around the uppercase result.
27.13 Decorator Order
```Decorator order affects the final result. When several decorators are stacked, Python applies them from bottom to top. However, when the decorated function runs, the outermost wrapper begins first.
Understanding order is important when decorators transform return values, check permissions, open resources, or display messages. Reversing the order can produce a different result.
Example
from functools import wraps
```
def first(function):
@wraps(function)
def wrapper():
print("First decorator: before")
```
function()
print("First decorator: after")
return wrapper
```
def second(function):
@wraps(function)
def wrapper():
print("Second decorator: before")
```
function()
print("Second decorator: after")
return wrapper
```
@first
@second
def task():
print("Original task")
task()
```
Output
First decorator: before
```
Second decorator: before
Original task
Second decorator: after
First decorator: after
```
Output Explanation
The second decorator is applied first because it is closest to the function. The first decorator wraps the result. During execution, the outer first wrapper starts before the inner second wrapper.
27.14 Class Decorators
```
A decorator can be implemented as a class instead of a function. The class receives the original function in its constructor. It then defines a __call__() method so instances of the class can behave like callable functions.
Class decorators are useful when the decorator needs to store state, such as the number of times a function was called. The stored information can remain available between calls.
Example
class CallCounter:
def __init__(self, function):
self.function = function
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print("Call number:", self.count)
return self.function(*args, **kwargs)
```
@CallCounter
def greet(name):
print("Hello,", name)
greet("Ali")
greet("Sara")
greet("Michael")
```
Output
Call number: 1
```
Hello, Ali
Call number: 2
Hello, Sara
Call number: 3
Hello, Michael
```
Output Explanation
The class stores the original function and a call counter. Each time the decorated function runs, __call__() increases the counter before calling the original function.
27.15 Decorating Methods
```
Methods inside classes can also be decorated. The wrapper must accept self along with any other arguments. Using *args and **kwargs automatically includes the instance argument.
Method decorators can log object actions, validate method arguments, check permissions, measure performance, or prevent methods from running under certain conditions.
Example
from functools import wraps
```
def log_method(function):
@wraps(function)
def wrapper(*args, **kwargs):
print("Running method:", function.**name**)
```
result = function(*args, **kwargs)
print("Method finished:", function.__name__)
return result
return wrapper
```
class BankAccount:
def **init**(self, balance):
self.balance = balance
```
@log_method
def deposit(self, amount):
self.balance += amount
return self.balance
```
account = BankAccount(500)
print("New balance:", account.deposit(200))
```
Output
Running method: deposit
```
Method finished: deposit
New balance: 700
```
Output Explanation
The decorator logs the method name before and after the deposit. The original method updates the account balance and returns the new value.
```27.16 Caching Decorators
```Caching stores the result of a function call so the program can reuse it when the same inputs appear again. This can improve performance when a function performs an expensive calculation or repeatedly requests the same data.
Python provides functools.cache and functools.lru_cache. The lru_cache decorator can limit how many results are stored. Cached function arguments generally need to be hashable values such as numbers, strings, and tuples.
Example
from functools import lru_cache
```
@lru_cache(maxsize=4)
def calculate_square(number):
print("Calculating square of", number)
return number ** 2
print(calculate_square(5))
print(calculate_square(5))
print(calculate_square(10))
print(calculate_square(10))
```
Output
Calculating square of 5
```
25
25
Calculating square of 10
100
100
```
Output Explanation
The first call for each number performs the calculation and stores the result. The second call with the same number returns the cached result, so the calculation message does not appear again.
```27.17 Timing Decorators
```A timing decorator measures how long a function takes to run. It records the time before calling the function, records the time afterward, and calculates the difference.
Timing decorators are useful for comparing algorithms, locating slow code, testing performance, and monitoring application operations. The time.perf_counter() function provides a precise timer for this purpose.
Example
import time
```
from functools import wraps
def measure_time(function):
@wraps(function)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
```
result = function(*args, **kwargs)
end_time = time.perf_counter()
duration = end_time - start_time
print(function.__name__, "took", duration, "seconds")
return result
return wrapper
```
@measure_time
def calculate_total():
total = 0
```
for number in range(1, 1000001):
total += number
return total
```
print("Total:", calculate_total())
```
Example Output
calculate_total took 0.0524812000003 seconds
```
Total: 500000500000
```
Output Explanation
The decorator measures the duration of the calculation and prints it. The exact execution time will differ depending on the computer and current system activity. The original return value is preserved.
```27.18 Authentication Decorators
```An authentication decorator checks whether a user is allowed to run a function. It may verify that a user is logged in, has a required role, or owns a specific permission.
Web frameworks frequently use decorators for access control. A decorator can stop a protected function from running and return an error message when the user does not meet the requirements.
Example
from functools import wraps
```
current_user = {
"name": "Sara",
"logged_in": True,
"role": "admin"
}
def require_admin(function):
@wraps(function)
def wrapper(*args, **kwargs):
if not current_user["logged_in"]:
return "Access denied: Please log in."
```
if current_user["role"] != "admin":
return "Access denied: Administrator role required."
return function(*args, **kwargs)
return wrapper
```
@require_admin
def delete_record(record_id):
return f"Record {record_id} was deleted."
print(delete_record(105))
```
Output
Record 105 was deleted.
Output Explanation
The decorator first checks whether the user is logged in and whether the role is admin. Because both conditions are true, the protected function runs and deletes the selected record.
27.19 Practical Decorator Applications
```Decorators can solve many real programming problems. They are commonly used for logging, validation, retries, caching, timing, access control, formatting, counting calls, handling exceptions, and checking application state.
In this example, a validation decorator checks that a product price is positive. The decorator prevents the original function from running when the value is invalid.
Example: Price Validation Decorator
from functools import wraps
```
def require_positive_price(function):
@wraps(function)
def wrapper(product_name, price):
if not isinstance(price, (int, float)):
return "Error: Price must be a number."
```
if price <= 0:
return "Error: Price must be greater than zero."
return function(product_name, price)
return wrapper
```
@require_positive_price
def create_product(product_name, price):
return f"{product_name} was created with a price of ${price:.2f}."
print(create_product("Keyboard", 49.99))
print(create_product("Monitor", -100))
print(create_product("Mouse", "twenty"))
```
Output
Keyboard was created with a price of $49.99.
```
Error: Price must be greater than zero.
Error: Price must be a number.
```
Output Explanation
The valid keyboard price passes the checks and reaches the original function. The negative monitor price and text mouse price are rejected by the decorator before the product function runs.
```27.20 Chapter Practice Exercises
```These exercises reinforce function objects, nested functions, closures, decorators, decorator arguments, metadata preservation, multiple decorators, class decorators, caching, timing, authentication, and validation.
- Store a function in a variable and call it through the variable.
- Pass a function as an argument to another function.
- Create an outer function containing a nested function.
- Create a closure that remembers a greeting word.
- Create a decorator that prints a message before a function runs.
- Create a decorator that prints messages before and after a function.
- Apply a decorator manually without using
@. - Apply the same decorator using
@syntax. - Create a decorator that converts a returned string to uppercase.
- Create a decorator that repeats a function five times.
- Create a decorator that receives a custom title.
- Create a wrapper using
*argsand**kwargs. - Create a decorator that logs function arguments.
- Preserve metadata using
functools.wraps. - Apply two decorators to one function.
- Reverse the decorator order and compare the output.
- Create a class decorator that counts function calls.
- Decorate a class method.
- Cache a calculation using
lru_cache. - Create a timing decorator.
- Create a login-check decorator.
- Create a role-check decorator.
- Create a decorator that catches exceptions.
- Create a decorator that validates positive numbers.
- Create a decorator that retries a function three times.
Practice Example: Return Value Logger
from functools import wraps
```
def log_result(function):
@wraps(function)
def wrapper(*args, **kwargs):
result = function(*args, **kwargs)
```
print("Function:", function.__name__)
print("Returned:", result)
return result
return wrapper
```
@log_result
def multiply(first_number, second_number):
return first_number * second_number
answer = multiply(6, 7)
print("Answer:", answer)
```
Output
Function: multiply
```
Returned: 42
Answer: 42
```
Output Explanation
The decorator calls the original function and stores its result. It displays the function name and returned value before returning the result to the calling code.
```27.21 Chapter Mini Project
```Project: Decorator-Based Banking Security System
In this mini project, you will create a small banking system that uses several decorators. One decorator checks whether the user is logged in. Another checks whether the user has permission to perform administrative actions. A third logs function calls, and a fourth validates transaction amounts.
This project combines decorator arguments, *args, **kwargs, functools.wraps, methods, validation, authentication, authorization, logging, and return values.
Complete Program
from functools import wraps
```
from datetime import datetime
current_user = {
"username": "sara",
"logged_in": True,
"role": "admin"
}
activity_log = []
def log_activity(function):
"""Record when a decorated function is called."""
```
@wraps(function)
def wrapper(*args, **kwargs):
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = (
f"{current_time} | "
f"User: {current_user['username']} | "
f"Action: {function.__name__}"
)
activity_log.append(log_entry)
print("LOG:", log_entry)
return function(*args, **kwargs)
return wrapper
```
def require_login(function):
"""Allow the function to run only for logged-in users."""
```
@wraps(function)
def wrapper(*args, **kwargs):
if not current_user["logged_in"]:
return "Access denied: You must log in."
return function(*args, **kwargs)
return wrapper
```
def require_role(required_role):
"""Allow the function to run only for a selected role."""
```
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
if current_user["role"] != required_role:
return (
"Access denied: "
f"The {required_role} role is required."
)
return function(*args, **kwargs)
return wrapper
return decorator
```
def validate_amount(function):
"""Check that a transaction amount is a positive number."""
```
@wraps(function)
def wrapper(self, amount):
if not isinstance(amount, (int, float)):
return "Transaction failed: Amount must be a number."
if amount <= 0:
return "Transaction failed: Amount must be greater than zero."
return function(self, amount)
return wrapper
```
class BankAccount:
def **init**(self, owner, balance=0):
self.owner = owner
self.balance = balance
```
@require_login
@log_activity
def view_balance(self):
return f"{self.owner}'s balance is ${self.balance:.2f}."
@require_login
@validate_amount
@log_activity
def deposit(self, amount):
self.balance += amount
return (
f"Deposit successful. "
f"New balance: ${self.balance:.2f}."
)
@require_login
@validate_amount
@log_activity
def withdraw(self, amount):
if amount > self.balance:
return "Transaction failed: Insufficient funds."
self.balance -= amount
return (
f"Withdrawal successful. "
f"New balance: ${self.balance:.2f}."
)
@require_login
@require_role("admin")
@log_activity
def reset_balance(self):
self.balance = 0
return "The account balance was reset to $0.00."
```
account = BankAccount("Sara", 1000)
print("BANK ACCOUNT SYSTEM")
print("----------------------------------------")
print(account.view_balance())
print()
print(account.deposit(250))
print()
print(account.withdraw(400))
print()
print(account.withdraw(2000))
print()
print(account.deposit(-50))
print()
print(account.reset_balance())
print()
print(account.view_balance())
print()
print("ACTIVITY LOG")
print("----------------------------------------")
for entry in activity_log:
print(entry)
```
Example Output
BANK ACCOUNT SYSTEM
```
---
LOG: 2026-07-19 12:30:00 | User: sara | Action: view_balance
Sara's balance is $1000.00.
LOG: 2026-07-19 12:30:00 | User: sara | Action: deposit
Deposit successful. New balance: $1250.00.
LOG: 2026-07-19 12:30:00 | User: sara | Action: withdraw
Withdrawal successful. New balance: $850.00.
LOG: 2026-07-19 12:30:00 | User: sara | Action: withdraw
Transaction failed: Insufficient funds.
Transaction failed: Amount must be greater than zero.
LOG: 2026-07-19 12:30:00 | User: sara | Action: reset_balance
The account balance was reset to $0.00.
LOG: 2026-07-19 12:30:00 | User: sara | Action: view_balance
Sara's balance is $0.00.
## ACTIVITY LOG
2026-07-19 12:30:00 | User: sara | Action: view_balance
2026-07-19 12:30:00 | User: sara | Action: deposit
2026-07-19 12:30:00 | User: sara | Action: withdraw
2026-07-19 12:30:00 | User: sara | Action: withdraw
2026-07-19 12:30:00 | User: sara | Action: reset_balance
2026-07-19 12:30:00 | User: sara | Action: view_balance
```
Project Explanation
The log_activity decorator records the date, time, username, and function name. It stores each entry in the activity_log list and displays it when the function runs.
The require_login decorator checks the logged_in value before allowing protected methods to run. When the user is not logged in, the decorator returns an access-denied message.
The require_role() decorator receives the required role as an argument. It creates and returns another decorator. The reset_balance() method requires the admin role.
The validate_amount decorator checks that deposit and withdrawal amounts are numeric and greater than zero. Invalid amounts are rejected before the original transaction method runs.
Decorator order controls which checks happen first. For example, the login decorator is outside the validation and logging decorators. This prevents unauthorized users from reaching the protected transaction logic.
How to Run the Mini Project
- Open Visual Studio Code, IDLE, PyCharm, or another Python editor.
- Create a new file named
decorator_banking_system.py. - Copy the complete project code into the file.
- Save the file.
- Open a terminal in the folder containing the file.
- Run
python decorator_banking_system.py. - On some computers, run
python3 decorator_banking_system.py. - Review the transaction results.
- Review the activity log displayed at the end.
- Change
logged_intoFalseand run the program again. - Change the role from
admintocustomerand test the reset operation.
Project Challenges
- Add a decorator that limits the number of withdrawal attempts.
- Add a decorator that requires a PIN number.
- Add a decorator that measures transaction execution time.
- Add a decorator that catches unexpected errors.
- Save the activity log to a text file.
- Add transfer functionality between two accounts.
- Require the admin role to view every user's activity log.
- Add a decorator that blocks transactions above a selected amount.
- Add daily withdrawal limits.
- Add a class decorator that counts all account operations.
- Cache selected account reports.
- Create separate roles for customers, employees, and administrators.
A modern course built to help learners study step by step with clarity, comfort, and confidence.