python-mastery/Exercises/ex7_1.md

138 lines
3.2 KiB
Markdown
Raw Normal View History

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:*
- Learn how to define simple decorator functions.
2023-07-17 03:21:00 +02:00
*Files Created:* `logcall.py`
*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.5](ex6_5.md), you created a callable class `ValidatedFunction` that
2023-07-17 03:21:00 +02:00
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
![](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/)