Register to get access to free programming courses with interactive exercises

More about decorators Python: Functions

Let us do a little more fantasizing. Imagine we want to be able to validate arguments of functions — check whether their values correspond to rules. And we want to do it using decorators that can be applied again. We will implement a couple of these decorators by the end of the lesson.

Using decorators with parameters

But first, we need to digress a little. What happens if the arguments of the function do not pass our verification? We need to show the error. But how do we do it? We will tell you more about working with errors in the subsequent, but for now, we will show you how to provoke an error:

raise ValueError('Value too low!')
# From the traceback, the most recent calls are the last:
#   File "<stdin>", line 1, in <module>
# ValueError: Value is too low!

It is the error we will show if the argument value does not pass validation. Now we can start creating decorators. Let us say a function has a numeric argument greater than zero and is not equal to invalid values.

Of course, we could make this sort of special decorators:

def function(arg):
    # …

But that is not enough for all instances of such highly specialized decorators. We separate the wrapping of the function and the checks so that the usual predicates can act as the checks. But how does the decorator know about the predicate if it always accepts the wrapped function as a single parameter?

We can use closure. We need a function that will take a predicate function as an argument and return a wrapper function, and then it will also take a function as an argument and return the same function. Now Let us get down to writing this function layer cake:

def checking_that_arg_is(predicate, error_message):
    def wrapper(function):
        def inner(arg):
            if not predicate(arg):
                raise ValueError(error_message)
            return function(arg)
        return inner
    return wrapper

The function checking_that_arg_is takes a predicate and returns wrapper. Here, wrapper is now our decorator with inner inside. The inner checks the argument using a predicate. If we meet the condition, we call it function.

Over time you will be able to read and write this sort of code quickly and easily because decorators, including those with parameters, are often seen in Python code. Using a decorator with parameters looks like this:

@checking_that_arg_is(condition, "Invalid value!")
def foo(arg):
    # …

At last, we have something to wrap. Now we will write some closures that will act as checks:

def greater_than(value):
    def predicate(arg):
        return arg > value
    return predicate

def in_(*values):

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing:">Bookmate</span>
<span class="translation_missing" title="translation missing:">Healthsamurai</span>
<span class="translation_missing" title="translation missing:">Dualboot</span>
<span class="translation_missing" title="translation missing:">Abbyy</span>
Suggested learning programs
Developing web applications with Django
10 months
from scratch
under development
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.