Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are these type of python decorators written?

I'd like to write a decorator that would limit the number of times a function can be executed, something along the following syntax :


@max_execs(5)
def my_method(*a,**k):
   # do something here
   pass

I think it's possible to write this type of decorator, but I don't know how. I think a function won't be this decorator's first argument, right? I'd like a "plain decorator" implementation, not some class with a call method.

The reason for this is to learn how they are written. Please explain the syntax, and how that decorator works.

like image 692
Geo Avatar asked Jul 09 '09 20:07

Geo


People also ask

What are the types of decorators in Python?

In fact, there are two types of decorators in Python — class decorators and function decorators — but I will focus on function decorators here.

How is decorator used in Python?

A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.

What is the Python decorator give an example?

You'll use a decorator when you need to change the behavior of a function without modifying the function itself. A few good examples are when you want to add logging, test performance, perform caching, verify permissions, and so on. You can also use one when you need to run the same code on multiple functions.

What is decorator pattern in Python?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.


2 Answers

This is what I whipped up. It doesn't use a class, but it does use function attributes:

def max_execs(n=5):
    def decorator(fn):
        fn.max = n
        fn.called = 0
        def wrapped(*args, **kwargs):
            fn.called += 1
            if fn.called <= fn.max:
                return fn(*args, **kwargs)
            else:
                # Replace with your own exception, or something
                # else that you want to happen when the limit
                # is reached
                raise RuntimeError("max executions exceeded")
        return wrapped
    return decorator

max_execs returns a functioned called decorator, which in turn returns wrapped. decoration stores the max execs and current number of execs in two function attributes, which then get checked in wrapped.

Translation: When using the decorator like this:

@max_execs(5)
def f():
    print "hi!"

You're basically doing something like this:

f = max_execs(5)(f)
like image 100
mipadi Avatar answered Sep 24 '22 14:09

mipadi


Decorator is merely a callable that transforms a function into something else. In your case, max_execs(5) must be a callable that transforms a function into another callable object that will count and forward the calls.

class helper:
    def __init__(self, i, fn):
        self.i = i
        self.fn = fn
    def __call__(self, *args, **kwargs):
        if self.i > 0:
            self.i = self.i - 1
            return self.fn(*args, **kwargs)

class max_execs:
    def __init__(self, i):
        self.i = i
    def __call__(self, fn):
        return helper(self.i, fn)

I don't see why you would want to limit yourself to a function (and not a class). But if you really want to...

def max_execs(n):
    return lambda fn, i=n: return helper(i, fn)
like image 40
avakar Avatar answered Sep 23 '22 14:09

avakar