Prevent calling a function more than once if the parameters have been used before

Question:

I would like a way to limit the calling of a function to once per values of parameters.

For example

def unique_func(x):
    return x

>>> unique_func([1])
[1]
>>> unique_func([1])
*** wont return anything ***
>>> unique_func([2])
[2]

Any suggestions? I’ve looked into using memoization but not established a solution just yet.

This is not solved by the suggested Prevent a function from being called twice in a row since that only solves when the previous func call had them parameters.

Asked By: 0xgareth

||

Answers:

Memoization uses a mapping of arguments to return values. Here, you just want a mapping of arguments to None, which can be handled with a simple set.

def idempotize(f):
    cache = set()
    def _(x):
        if x in cache:
            return
        cache.add(x)
        return f(x)
    return _


@idempotize
def unique_fun(x):
    ...

With some care, this can be generalized to handle functions with multiple arguments, as long as they are hashable.

def idempotize(f):
    cache = set()
    def _(*args, **kwargs):
        k = (args, frozenset(kwargs.items()))
        if k in cache:
            return
        return f(*args, **kwargs)
    return _
Answered By: chepner

Consider using the built-in functools.lru_cache() instead of rolling your own.

It won’t return nothing on the second function call with the same arugments (it will return the same thing as the first function call) but maybe you can live with that. It would seem like a negligible price to pay, compared to the advantages of using something that’s maintained as part of the standard library.

Requires your argument x to be hashable, so won’t work with lists. Strings are fine.

from functools import lru_cache

@lru_cache()
def unique_fun(x):
    ...

I’ve built a function decorator to handle this scenario, that limits function calls to the same function in a given timeframe.

You can directly use it via PyPI with pip install ofunctions.threading or checkout the github sources.

Example: I want to limit calls to the same function with the same parameters to one call per 10 seconds:

from ofunctions.threading import no_flood

@no_flood(10)
def my_function():
    print("It's me, the function")

for _ in range(0, 5):
    my_function()

# Will print the text only once.

if after 10 seconds the function is called again, we’ll allow a new execution, but will prevent any other execution for the next 10 seconds.

By default @no_flood will limit function calls with the same parameter, so that calling func(1) and func(2) are still allowed concurrently.

The @no_flood decorator can also limit all function calls to a given function regardless of it’s parameters:

from ofunctions.threading import no_flood

@no_flood(10, False)
def my_function(var):
    print("It's me, function number {}".format(var))

for i in range(0, 5):
    my_function(i)

# Will only print function text once
Answered By: Orsiris de Jong
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.