Skip to content

Latest commit

 

History

History
137 lines (105 loc) · 3.2 KB

ex7_1.md

File metadata and controls

137 lines (105 loc) · 3.2 KB

[ Index | Exercise 6.5 | Exercise 7.2 ]

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:

# 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
>>> 

(b) A Real Decorator

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