|
| 1 | +--- |
| 2 | +Title: 'update_wrapper()' |
| 3 | +Description: 'Updates a wrapper function to look like the wrapped function by copying attributes like `__name__`, `__doc__`, and `__module__`.' |
| 4 | +Subjects: |
| 5 | + - 'Computer Science' |
| 6 | + - 'Data Science' |
| 7 | + - 'Web Development' |
| 8 | +Tags: |
| 9 | + - 'Best Practices' |
| 10 | + - 'Decorators' |
| 11 | + - 'Functions' |
| 12 | + - 'Metadata' |
| 13 | +CatalogContent: |
| 14 | + - 'learn-python-3' |
| 15 | + - 'paths/computer-science' |
| 16 | +--- |
| 17 | + |
| 18 | +The **`update_wrapper()`** function from the `functools` module updates a wrapper function to look like the wrapped function by copying attributes such as `__name__`, `__doc__`, and `__module__`. This is essential for creating decorators that preserve the original function's metadata, making debugging and introspection much easier. |
| 19 | + |
| 20 | +## Syntax |
| 21 | + |
| 22 | +```pseudo |
| 23 | +from functools import update_wrapper |
| 24 | +
|
| 25 | +update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) |
| 26 | +``` |
| 27 | + |
| 28 | +**Parameters:** |
| 29 | + |
| 30 | +- `wrapper`: The function that is acting as a wrapper (e.g., inside a decorator). |
| 31 | +- `wrapped`: The original function being wrapped. |
| 32 | +- `assigned`: A tuple of attribute names to copy (defaults to `WRAPPER_ASSIGNMENTS`). |
| 33 | +- `updated`: A tuple of attribute names to update (defaults to `WRAPPER_UPDATES`). |
| 34 | + |
| 35 | +**Return value:** |
| 36 | + |
| 37 | +The `update_wrapper()` function returns the `wrapper` function itself, after updating it with the attributes of `wrapped`. |
| 38 | + |
| 39 | +## Example |
| 40 | + |
| 41 | +This example demonstrates how `update_wrapper()` preserves the original function's metadata when creating a decorator: |
| 42 | + |
| 43 | +```py |
| 44 | +from functools import update_wrapper |
| 45 | + |
| 46 | +def my_decorator(func): |
| 47 | + def wrapper(*args, **kwargs): |
| 48 | + print(f"Calling {func.__name__}") |
| 49 | + return func(*args, **kwargs) |
| 50 | + |
| 51 | + # Update the wrapper to look like the original function |
| 52 | + update_wrapper(wrapper, func) |
| 53 | + return wrapper |
| 54 | + |
| 55 | +@my_decorator |
| 56 | +def greet(name): |
| 57 | + """A simple greeting function.""" |
| 58 | + return f"Hello, {name}!" |
| 59 | + |
| 60 | +# Check that metadata is preserved |
| 61 | +print(f"Function name: {greet.__name__}") |
| 62 | +print(f"Function docstring: {greet.__doc__}") |
| 63 | +``` |
| 64 | + |
| 65 | +Here is the output: |
| 66 | + |
| 67 | +```shell |
| 68 | +Function name: greet |
| 69 | +Function docstring: A simple greeting function. |
| 70 | +``` |
| 71 | + |
| 72 | +## Codebyte Example |
| 73 | + |
| 74 | +This codebyte example shows the difference between using and not using `update_wrapper()`: |
| 75 | + |
| 76 | +```codebyte/python |
| 77 | +from functools import update_wrapper |
| 78 | +
|
| 79 | +def decorator_without_update(func): |
| 80 | + def wrapper(*args, **kwargs): |
| 81 | + print(f"Executing {func.__name__}") |
| 82 | + return func(*args, **kwargs) |
| 83 | + return wrapper |
| 84 | +
|
| 85 | +def decorator_with_update(func): |
| 86 | + def wrapper(*args, **kwargs): |
| 87 | + print(f"Executing {func.__name__}") |
| 88 | + return func(*args, **kwargs) |
| 89 | +
|
| 90 | + # This preserves the original function's metadata |
| 91 | + update_wrapper(wrapper, func) |
| 92 | + return wrapper |
| 93 | +
|
| 94 | +@decorator_without_update |
| 95 | +def function1(): |
| 96 | + """This is function1.""" |
| 97 | + return "Hello from function1" |
| 98 | +
|
| 99 | +@decorator_with_update |
| 100 | +def function2(): |
| 101 | + """This is function2.""" |
| 102 | + return "Hello from function2" |
| 103 | +
|
| 104 | +# Check metadata preservation |
| 105 | +print("Without update_wrapper:") |
| 106 | +print(f"Name: {function1.__name__}") |
| 107 | +print(f"Docstring: {function1.__doc__}") |
| 108 | +
|
| 109 | +print("\nWith update_wrapper:") |
| 110 | +print(f"Name: {function2.__name__}") |
| 111 | +print(f"Docstring: {function2.__doc__}") |
| 112 | +
|
| 113 | +# Call the functions |
| 114 | +print(f"\nResult: {function1()}") |
| 115 | +print(f"Result: {function2()}") |
| 116 | +``` |
0 commit comments