Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute A Python decorator is a callable that transforms a function, method, or class when its definition is processed. The @decorator line is convenient syntax: Python creates the definition, passes it to the decorator, and binds the returned object to the original name. A decorator can wrap calls, register a function, attach attributes, or replace a class; it does not have to be a wrapper that runs on every call.
This guide explains the transformation model, metadata-preserving wrappers, configurable decorator factories, stacking order, practical use cases, testing, and common failure modes.
What @decorator means
Consider this definition:
@announce
def greet(name):
return f"Hello, {name}!"
Python treats it essentially as:
def greet(name):
return f"Hello, {name}!"
greet = announce(greet)
The decorator expression is evaluated and called with the newly created function. Whatever it returns becomes the value bound to greet. This rebinding happens while the module (or containing class body) is being executed, before a caller invokes greet.
The syntax keeps the transformation next to the declaration. It also works with any callable transformation, including a class, an object implementing __call__, or a factory that returns a decorator.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match#1 Best Overall
How a wrapper decorator works
A wrapper-based decorator has three jobs: accept the original function, define a replacement that adds behavior, and return that replacement.
from functools import wraps
def announce(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@announce
def greet(name):
return f"Hello, {name}!"
print(greet("Mina"))
Calling greet("Mina") actually calls wrapper. The wrapper logs the call, forwards positional and keyword arguments, and returns the original result. *args and **kwargs make the decorator usable with many function signatures; a specialized wrapper can instead declare explicit parameters when that improves validation or readability.
Why functools.wraps matters
Without @wraps(func), introspection sees the wrapper’s name, docstring, annotations, and other metadata rather than the decorated function’s. functools.wraps is intended for exactly this pattern. It copies selected attributes such as __name__, __qualname__, __module__, __annotations__, and __doc__, and updates the wrapper’s attribute dictionary. It also sets __wrapped__, allowing tools and other decorators to reach the original callable.
from inspect import signature
print(greet.__name__) # greet
print(greet.__doc__) # the original docstring, if present
print(signature(greet)) # commonly follows the wrapped function
Use wraps whenever you return a wrapper unless you have a specific reason not to. Preserving metadata helps debuggers, documentation generators, tracing tools, and tests.
Definition time versus call time
The decorator itself runs when Python executes the decorated definition. Code inside the returned wrapper runs each time the function is called. Keeping those moments separate prevents subtle bugs.
- Definition time: configuration is read, registration can occur, and the original function can be replaced.
- Call time: a wrapper can inspect arguments, enforce a policy, measure duration, catch or translate exceptions, and alter the return value.
- Neither is required: a decorator may simply attach an attribute or return a different object without wrapping calls.
For example, a registration decorator can collect functions for later dispatch:
Rank #2
COMMANDS = {}
def command(name):
def register(func):
COMMANDS[name] = func
return func
return register
@command("hello")
def hello_command():
return "Hello"
# The function remains callable, and registration happened at definition time.
print(COMMANDS["hello"]())
Parameterized decorators and decorator factories
If the decorator needs options, the expression after @ must first call a factory. That creates three layers:
- The factory receives configuration.
- The returned decorator receives the function.
- The returned wrapper receives runtime call arguments.
from functools import wraps
def announce_with(prefix): # 1. configuration
def decorator(func): # 2. function
@wraps(func)
def wrapper(*args, **kwargs): # 3. call arguments
print(f"{prefix}: {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@announce_with("AUDIT")
def add(a, b):
return a + b
print(add(2, 3))
@announce_with("AUDIT") is equivalent to add = announce_with("AUDIT")(add). A frequent mistake is omitting one layer and accidentally passing a string where Python expects a function, or returning the decorator instead of the wrapper.
Free tools Windows power users keep installed
One-click scans. No signup required.
Stacking decorators: which one runs first?
With stacked decorators, the decorator nearest the function is applied first:
@outer
a@inner
def task():
pass
Ignoring the typo-like spacing above, the intended form is:
@outer
@inner
def task():
pass
Its expansion is:
def task():
pass
task = outer(inner(task))
inner receives the original function. outer receives whatever inner returned. At call time, the outer wrapper normally executes first and can decide whether to call the inner one.
Order changes behavior. A cache placed outside an authorization check can return a cached result before authorization runs; placing authorization outside the cache checks every request. Write a small expansion on paper when order is not obvious, and test both success and failure paths.
Useful decorator patterns
Timing and logging
from functools import wraps
from time import perf_counter
def timed(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = perf_counter()
try:
return func(*args, **kwargs)
finally:
elapsed = perf_counter() - start
print(f"{func.__name__}: {elapsed:.6f}s")
return wrapper
The finally block records failures as well as successful calls. Avoid logging sensitive arguments by default.
Validation and authorization
A policy decorator can reject a call before invoking the function, or check a returned value afterward. Keep the contract explicit: document the exception raised, the required argument, and whether the wrapped function is called at all on failure.
Caching
Caching is a common use case when a function is deterministic for its inputs. Python’s standard library provides caching decorators; if you write your own, define the cache key rules, memory limits, invalidation behavior, and thread or process scope. Never cache values that depend on hidden mutable state unless that dependency is part of the invalidation design.
Method transformation
classmethod and staticmethod are built-in examples of decorators that change method binding. A class method receives the class as its first argument; a static method receives no implicit instance or class argument.
Recommended Free Tools
Registration and attributes
A decorator can add a marker attribute, register a callback, or return a replacement class. These transformations happen at definition time and may be preferable to a runtime wrapper when no per-call work is needed.
Writing robust decorators
- Use
@wrapsfor wrappers. - Forward arguments and return values unchanged unless changing them is the documented purpose.
- Preserve exceptions unless the decorator intentionally translates or handles them.
- Keep side effects visible; registration and configuration should not be surprising.
- Make decorator order part of the design and test it explicitly.
- Prefer a normal helper function when behavior is used once or when the wrapper would obscure control flow.
- For async functions, write an
async defwrapper andawaitthe original; a synchronous wrapper that merely returns a coroutine may put timing, exception handling, or context management in the wrong place.
Testing and troubleshooting
The function name or docstring changed
Add from functools import wraps and place @wraps(func) directly above the inner wrapper. Confirm that the decorator is wrapping the intended function rather than a second wrapper.
“Missing required positional argument” or unexpected keyword errors
The wrapper’s signature may not forward arguments correctly. Use *args, **kwargs and call func(*args, **kwargs), or deliberately mirror the original signature and test positional and keyword calls.
The configured decorator receives a function too early
Check the layers. @limit(3) requires limit to return a decorator; @limit requires limit itself to accept the function. Do not mix these forms.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Behavior runs in the wrong order
Expand the stack into assignments such as f = outer(inner(f)). Swap the lines if the policy, cache, transaction, or logging boundary is wrong, then add a regression test documenting the intended order.
Methods behave differently from functions
Remember that instance methods receive self after descriptor binding. A decorator that replaces a method with a plain object can prevent normal binding. Test access through an instance and through the class.
Debugging the original callable
inspect.unwrap(decorated_function) can follow the __wrapped__ chain created by wraps. This is useful for tests and introspection, but it does not undo side effects already performed by a decorator.
When a decorator is the right tool
Choose one when the same cross-cutting behavior belongs around multiple callables and placing that behavior beside each declaration improves comprehension. Logging, authorization, retries, caching, registration, and instrumentation are typical fits. Avoid decorators when the behavior needs many unrelated configuration values, has complicated branching that hides the business operation, or would be clearer as an explicit function call or context manager.
Best Value
A decorator is a small language feature with a broad contract: it transforms a definition, and the transformed object may wrap, register, annotate, or replace it. Knowing exactly which layer receives configuration, the function, and call arguments is the key to writing one correctly.
Or skip the browser setup: ScreenshotNeo for automated page captures
Decorators are useful for wrapping an HTTP client call that captures a page, but you do not need to configure a headless browser yourself. ScreenshotNeo provides a single screenshot API request:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for parameters and response details. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can a decorator be applied to a class?
Yes. A class decorator receives the class object created by the class statement and returns that class or a replacement. It can register the class, attach attributes, or alter its binding.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
What does a decorator return if it does not wrap a function?
It returns the object that should replace the original binding, which may be the unchanged function, a registered function, a descriptor, a class, or another callable.
How can I inspect several layers of decorators?
Use metadata-preserving wrappers and inspect the callable’s __wrapped__ chain or call inspect.unwrap. Without functools.wraps, that chain may not exist.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

