11.8. 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)
11.8.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()
11.8.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'
11.8.3. Use Case - 0x01¶
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
11.8.4. Use Case - 0x02¶
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>'
11.8.5. Use Case - 0x03¶
>>> 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 logged-in')
...
... def logout(self):
... self.is_authenticated = False
... print('User logged-out')
...
... @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 logged-in
>>>
>>> mark.change_password('Nasa69')
>>> mark.password
'Nasa69'
>>> mark.logout()
User logged-out
>>> mark.change_password('Ares3')
Traceback (most recent call last):
PermissionError
11.8.6. Assignments¶
"""
* Assignment: Decorator Method Syntax
* Complexity: easy
* Lines of code: 5 lines
* Time: 5 min
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
>>> 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():
...
"""
* Assignment: Decorator Method Alive
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min
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
>>> 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