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


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/FunctionsGroup -.-> python/keyword_arguments("`Keyword Arguments`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/BasicConceptsGroup -.-> python/numeric_types("`Numeric Types`") python/BasicConceptsGroup -.-> python/strings("`Strings`") python/ControlFlowGroup -.-> python/conditional_statements("`Conditional Statements`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/DataStructuresGroup -.-> python/dictionaries("`Dictionaries`") python/DataStructuresGroup -.-> python/sets("`Sets`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/FunctionsGroup -.-> python/default_arguments("`Default Arguments`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/using_packages("`Using Packages`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("`Classes and Objects`") python/ObjectOrientedProgrammingGroup -.-> python/constructor("`Constructor`") python/ObjectOrientedProgrammingGroup -.-> python/polymorphism("`Polymorphism`") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("`Encapsulation`") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("`Catching Exceptions`") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("`Raising Exceptions`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/keyword_arguments -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/variables_data_types -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/numeric_types -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/strings -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/conditional_statements -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/for_loops -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/lists -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/tuples -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/dictionaries -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/sets -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/function_definition -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/default_arguments -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/importing_modules -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/using_packages -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/standard_libraries -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/classes_objects -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/constructor -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/polymorphism -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/encapsulation -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/catching_exceptions -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/raising_exceptions -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/python_shell -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} python/build_in_functions -.-> lab-132514{{"`Define a Simple Decorator Functions`"}} end

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

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.

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