138 lines
3.2 KiB
Markdown
138 lines
3.2 KiB
Markdown
\[ [Index](index.md) | [Exercise 6.5](ex6_5.md) | [Exercise 7.2](ex7_2.md) \]
|
|
|
|
# Exercise 7.1
|
|
|
|
*Objectives:*
|
|
|
|
- Learn how to define simple decorator functions.
|
|
|
|
*Files Created:* `logcall.py`
|
|
|
|
*Files Modified:* `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 "<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/)
|