6.13. Decorate Method
mydecorator
is a decorator namemethod
is a method nameself
is an instanceargs
arbitrary number of positional argumentskwargs
arbitrary number of keyword arguments
SetUp:
>>> def mydecorator(method):
... ...
Syntax:
>>> class MyClass:
... @mydecorator
... def mymethod(self, *args, **kwargs):
... ...
Is equivalent to:
>>> class MyClass:
... def mymethod(self, *args, **kwargs):
... ...
>>>
>>>
>>> obj = MyClass()
>>> obj.mymethod = mydecorator(obj.mymethod)
6.13.1. Syntax
mydecorator
is a decorator namemymethod
is a method nameself
is an instanceargs
arbitrary number of positional argumentskwargs
arbitrary number of keyword arguments
>>> def mydecorator(method):
... def wrapper(self, *args, **kwargs):
... return method(self, *args, **kwargs)
... return wrapper
>>>
>>>
>>> class MyClass:
... @mydecorator
... def mymethod(self):
... ...
>>>
>>>
>>> my = MyClass()
>>> my.mymethod()
6.13.2. Example
>>> def run(method):
... def wrapper(self, *args, **kwargs):
... return method(self, *args, **kwargs)
... return wrapper
>>>
>>>
>>> class User:
... @run
... def say_hello(self, name):
... return f'My name... {name}'
>>>
>>>
>>> mark = User()
>>> mark.say_hello('José Jiménez')
'My name... José Jiménez'
6.13.3. Use Case - 1
Is Allowed
>>> def if_allowed(method):
... def wrapper(self, *args, **kwargs):
... if self._is_allowed:
... return method(self, *args, **kwargs)
... else:
... print('Sorry, Permission Denied')
... return wrapper
>>>
>>>
>>> class MyClass:
... def __init__(self):
... self._is_allowed = True
...
... @if_allowed
... def do_something(self):
... print('Doing...')
...
... @if_allowed
... def do_something_else(self):
... print('Doing something else...')
>>>
>>>
>>> my = MyClass()
>>>
>>> my.do_something()
Doing...
>>> my.do_something_else()
Doing something else...
>>>
>>>
>>> my._is_allowed = False
>>>
>>> my.do_something()
Sorry, Permission Denied
>>> my.do_something_else()
Sorry, Permission Denied
6.13.4. Use Case - 2
Paragraph
>>> def paragraph(method):
... def wrapper(self, *args, **kwargs):
... result = method(self, *args, **kwargs)
... return f'<p>{result}</p>'
... return wrapper
>>>
>>>
>>> class HTMLReport:
... @paragraph
... def first(self, *args, **kwargs):
... return 'First'
...
... @paragraph
... def second(self, *args, **kwargs):
... return 'Second'
>>>
>>>
>>> x = HTMLReport()
>>>
>>> x.first()
'<p>First</p>'
>>>
>>> x.second()
'<p>Second</p>'
6.13.5. Use Case - 3
>>> def login_required(obj):
... def wrapper(instance, *args, **kwargs):
... if not instance.is_authenticated:
... raise PermissionError
... return obj(instance, *args, **kwargs)
... return wrapper
>>>
>>>
>>> class User:
... def __init__(self, username, password):
... self.username = username
... self.password = password
... self.is_authenticated = False
...
... def login(self):
... self.is_authenticated = True
... print('User login')
...
... def logout(self):
... self.is_authenticated = False
... print('User logout')
...
... @login_required
... def change_password(self, new_password):
... self.password = new_password
>>> mark = User('mwatney', 'Ares3')
>>> mark.password
'Ares3'
>>> mark.change_password('Nasa69')
Traceback (most recent call last):
PermissionError
>>> mark.login()
User login
>>>
>>> mark.change_password('Nasa69')
>>> mark.password
'Nasa69'
>>> mark.logout()
User logout
>>> mark.change_password('Ares3')
Traceback (most recent call last):
PermissionError
6.13.6. 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 Method Syntax
# - Difficulty: easy
# - Lines: 5
# - Minutes: 3
# %% English
# 1. Create method decorator `mydecorator`
# 2. Decorator should have `wrapper` with `*args` and `**kwargs` parameters
# 3. Wrapper should call original method with it's original parameters,
# and return its value
# 4. Decorator should return `wrapper` method
# 5. Run doctests - all must succeed
# %% Polish
# 1. Stwórz dekorator metod `mydecorator`
# 2. Dekorator powinien mieć `wrapper` z parametrami `*args` i `**kwargs`
# 3. Wrapper powinien wywoływać oryginalną funkcję z jej oryginalnymi
# parametrami i zwracać jej wartość
# 4. Decorator powinien zwracać metodę `wrapper`
# 5. 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 method as an argument'
>>> class MyClass:
... @mydecorator
... def echo(self, text):
... return text
>>> my = MyClass()
>>> my.echo('hello')
'hello'
"""
# type: Callable[[Callable], Callable]
def mydecorator():
...
# %% 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 Method Alive
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3
# %% English
# 1. Create `if_alive` method decorator
# 2. Decorator will allow running `make_damage` method
# only if `current_health` is greater than 0
# 3. Run doctests - all must succeed
# %% Polish
# 1. Stwórz dekorator metody `if_alive`
# 2. Dekorator pozwoli na wykonanie metody `make_damage`,
# tylko gdy `current_health` jest większe niż 0
# 3. 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(if_alive), \
'Create if_alive() function'
>>> assert isfunction(if_alive(lambda: ...)), \
'if_alive() should take method as an argument'
>>> class Hero:
... def __init__(self, name):
... self.name = name
... self.current_health = 100
...
... @if_alive
... def make_damage(self):
... return 10
>>> hero = Hero('Mark Watney')
>>> hero.make_damage()
10
>>> hero.current_health = -10
>>> hero.make_damage()
Traceback (most recent call last):
RuntimeError: Hero is dead and cannot make damage
"""
# type: Callable[[Callable], Callable]
def if_alive(method):
def wrapper(hero, *args, **kwargs):
return method(hero, *args, **kwargs)
return wrapper