import functools


def call_only_once(f):
    """Wrapper that makes sure a function is called only once.
    This is useful for example if it's known that a very 
    expensive function is run many times but with the same 
    parameters and return value. It's not memoization since
    this wrapper doesn't take the paramters into consideration.
    The parameters used on the first call dictates what value is 
    stored and returned upon all future calls. 

    This is thus not the same as memoization where the cache 
    takes parameter values into consideration. The reason this was 
    used instead of memoization was that the parameters to a target
    function weren't hashable and able to be used in memoization.
    """
    result = None
    
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        nonlocal result
        if not result:
            result = f(*args, **kwds)
        return result

    return wrapper