Define a Simple Decorator Functions

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

Objectives:

  • Learn how to define a simple decorator functions.

Files Created: logcall.py

Files Modifie: validate.py

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
>>>
âœĻ Check Solution and Practice

A Real Decorator

Hint: Complete the following in the validate.py file

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.

âœĻ Check Solution and Practice

Summary

Congratulations! You have completed the Define a Simple Decorator Functions lab. You can practice more labs in LabEx to improve your skills.

Other Python Tutorials you may like