6.15. Decorator Wraps
from functools import wraps
@wraps(func)
6.15.1. Problem
Decorator will replace function with wrapper
All function attributes, such as
__doc__
and__name__
will be from decorator
>>> def mydecorator(func):
... def wrapper(*args, **kwargs):
... """wrapper docstring"""
... return func(*args, **kwargs)
... return wrapper
>>>
>>>
>>> @mydecorator
... def myfunction(x):
... """myfunction docstring"""
... print(x)
>>>
>>>
>>> print(myfunction.__name__)
wrapper
>>>
>>> print(myfunction.__doc__)
wrapper docstring
6.15.2. Solution: Manual
>>> def mydecorator(func):
... def wrapper(*args, **kwargs):
... """wrapper docstring"""
... return func(*args, **kwargs)
... wrapper.__name__ = func.__name__
... wrapper.__doc__ = func.__doc__
... return wrapper
>>>
>>>
>>> @mydecorator
... def myfunction(x):
... """myfunction docstring"""
... print(x)
>>>
>>>
>>> print(myfunction.__name__)
myfunction
>>>
>>> print(myfunction.__doc__)
myfunction docstring
6.15.3. Solution: Wraps
>>> from functools import wraps
>>>
>>>
>>> def mydecorator(func):
... @wraps(func)
... def wrapper(*args, **kwargs):
... """wrapper docstring"""
... return func(*args, **kwargs)
... return wrapper
>>>
>>>
>>> @mydecorator
... def myfunction(x):
... """myfunction docstring"""
... print(x)
>>>
>>>
>>> print(myfunction.__name__)
myfunction
>>>
>>> print(myfunction.__doc__)
myfunction docstring
6.15.4. Class Decorators
>>> def debug(cls):
... class Wrapper(cls):
... def __init__(self):
... print(f'debug: {cls.__name__}')
... Wrapper.__name__ = cls.__name__
... Wrapper.__doc__ = cls.__doc__
... return Wrapper
>>> @debug # User = debug(User)
... class User:
... pass
>>> mark = User()
debug: User
>>> print(User.__name__)
User
6.15.5. Assignments
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Decorator Functools Func
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% English
# 1. Use `functools.wraps` in correct place
# 2. Run doctests - all must succeed
# %% Polish
# 1. Użyj `functools.wraps` w odpowiednim miejscu
# 2. Uruchom doctesty - wszystkie muszą się powieść
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction
>>> assert isfunction(mydecorator), \
'Create mydecorator() function'
>>> assert isfunction(mydecorator(lambda: ...)), \
'mydecorator() should take function as an argument'
>>> @mydecorator
... def hello():
... '''Hello Docstring'''
>>> hello.__name__
'hello'
>>> hello.__doc__
'Hello Docstring'
"""
from functools import wraps
# type: Callable[[Callable], Callable]
def mydecorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Decorator Functools Args
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% English
# 1. Use `functools.wraps` in correct place
# 2. Run doctests - all must succeed
# %% Polish
# 1. Użyj `functools.wraps` w odpowiednim miejscu
# 2. Uruchom doctesty - wszystkie muszą się powieść
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction
>>> assert isfunction(mydecorator), \
'Create mydecorator() function'
>>> assert isfunction(mydecorator(True)), \
'mydecorator() should take one positional argument'
>>> assert isfunction(mydecorator(happy=True)), \
'mydecorator() should take one keyword argument'
>>> assert isfunction(mydecorator(happy=True)(lambda: ...)), \
'The result of mydecorator() should take function as an argument'
>>> @mydecorator(happy=False)
... def hello():
... '''Hello Docstring'''
>>> hello.__name__
'hello'
>>> hello.__doc__
'Hello Docstring'
"""
from functools import wraps
# type: Callable[[bool], Callable]
def mydecorator(happy=True):
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Decorator Functools Cls
# - Difficulty: easy
# - Lines: 2
# - Minutes: 3
# %% English
# 1. Modify code to restore docstring and name from decorated class
# 2. Run doctests - all must succeed
# %% Polish
# 1. Zmodyfikuj kod aby przywrócić docstring oraz nazwę z dekorowanej klasy
# 2. Uruchom doctesty - wszystkie muszą się powieść
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction, isclass
>>> assert isfunction(mydecorator), \
'Create mydecorator() function'
>>> assert mydecorator(object), \
'mydecorator() should take class as an argument'
>>> assert isclass(mydecorator(object)), \
'The result of mydecorator() should be a class'
>>> @mydecorator
... class Hello:
... '''Hello Docstring'''
>>> hello = Hello()
>>> hello.__name__
'Hello'
>>> hello.__doc__
'Hello Docstring'
"""
# type: Callable[[Type], Type]
def mydecorator(cls):
class Wrapper(cls):
pass
return Wrapper