5.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)
5.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()
5.8.2. Example¶
>>> def run(method):
... def wrapper(self, *args, **kwargs):
... return method(self, *args, **kwargs)
... return wrapper
>>>
>>>
>>> class Astronaut:
... @run
... def hello(self, name):
... return f'My name... {name}'
>>>
>>>
>>> astro = Astronaut()
>>> astro.hello('José Jiménez')
'My name... José Jiménez'
5.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
5.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>'
5.8.5. 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