\[ [Index](index.md) | [Exercise 6.5](ex6_5.md) | [Exercise 7.2](ex7_2.md) \] # Exercise 7.1 *Objectives:* - Learn how to define a simple decorator functions. *Files Created:* `logcall.py` *Files Modifie:* `validate.py` ## (a) Your First Decorator To start with decorators, write a _very_ simple decorator function that simply prints out a message each time a function is called. Create a file `logcall.py` and define the following function: ```python # logcall.py def logged(func): print('Adding logging to', func.__name__) def wrapper(*args, **kwargs): print('Calling', func.__name__) return func(*args, **kwargs) return wrapper ``` Now, make a separate file `sample.py` and apply it to a few function definitions: ```python # sample.py from logcall import logged @logged def add(x,y): return x+y @logged def sub(x,y): return x-y ``` Test your code as follows: ```python >>> import sample Adding logging to add Adding logging to sub >>> sample.add(3,4) Calling add 7 >>> sample.sub(2,3) Calling sub -1 >>> ``` ## (b) A Real Decorator In [Exercise 6.6](ex6_6.md), you created a callable class `ValidatedFunction` that enforced type annotations. Rewrite this class as a decorator function called `validated`. It should allow you to write code like this: ```python from validate import Integer, validated @validated def add(x: Integer, y:Integer) -> Integer: return x + y @validated def pow(x: Integer, y:Integer) -> Integer: return x ** y ``` Here's how the decorated functions should work: ```python >>> add(2, 3) 5 >>> add('2', '3') Traceback (most recent call last): File "", line 1, in File "validate.py", line 75, in wrapper raise TypeError('Bad Arguments\n' + '\n'.join(errors)) TypeError: Bad Arguments x: Expected y: Expected >>> pow(2, 3) 8 >>> pow(2, -1) Traceback (most recent call last): File "", line 1, in File "validate.py", line 83, in wrapper raise TypeError(f'Bad return: {e}') from None TypeError: Bad return: Expected >>> ``` Your decorator should try to patch up the exceptions so that they show more useful information as shown. Also, the `@validated` decorator should work in classes (you don't need to do anything special). ```python class Stock: def __init__(self, name, shares, price): self.name = name self.shares = shares self.price = price @property def cost(self): return self.shares * self.price @validated def sell(self, nshares:PositiveInteger): self.shares -= nshares ``` Note: This part doesn't involve a lot of code, but there are a lot of low-level fiddly bits. The solution will look almost the same as for Exercise 6.6. Don't be shy about looking at solution code though. \[ [Solution](soln7_1.md) | [Index](index.md) | [Exercise 6.5](ex6_5.md) | [Exercise 7.2](ex7_2.md) \] ---- `>>>` Advanced Python Mastery `...` A course by [dabeaz](https://www.dabeaz.com) `...` Copyright 2007-2023 ![](https://i.creativecommons.org/l/by-sa/4.0/88x31.png). This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)