[ Index | Exercise 6.5 | Exercise 7.2 ]
Objectives:
- Learn how to define simple decorator functions.
Files Created: logcall.py
Files Modified: validate.py
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:
# 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:
# 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:
>>> import sample
Adding logging to add
Adding logging to sub
>>> sample.add(3,4)
Calling add
7
>>> sample.sub(2,3)
Calling sub
-1
>>>
In Exercise 6.6, 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:
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:
>>> 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).
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 | Index | Exercise 6.5 | Exercise 7.2 ]
>>>
Advanced Python Mastery
...
A course by dabeaz
...
Copyright 2007-2023
. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License