2023-07-17 03:21:00 +02:00
\[ [Index ](index.md ) | [Exercise 6.5 ](ex6_5.md ) | [Exercise 7.2 ](ex7_2.md ) \]
# Exercise 7.1
*Objectives:*
2023-07-26 20:44:31 +02:00
- Learn how to define simple decorator functions.
2023-07-17 03:21:00 +02:00
*Files Created:* `logcall.py`
2023-07-26 20:44:31 +02:00
*Files Modified:* `validate.py`
2023-07-17 03:21:00 +02:00
## (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 "< stdin > ", line 1, in < module >
File "validate.py", line 75, in wrapper
raise TypeError('Bad Arguments\n' + '\n'.join(errors))
TypeError: Bad Arguments
x: Expected < class ' int ' >
y: Expected < class ' int ' >
>>> pow(2, 3)
8
>>> pow(2, -1)
Traceback (most recent call last):
File "< stdin > ", line 1, in < module >
File "validate.py", line 83, in wrapper
raise TypeError(f'Bad return: {e}') from None
TypeError: Bad return: Expected < class ' int ' >
>>>
```
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
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License ](http://creativecommons.org/licenses/by-sa/4.0/ )