Python: functools

 

  1. lru_cache

     

     

    import functools, time
    
    
    @functools.lru_cache(maxsize=128, typed=False)
    def b(x, y, z=3):
        time.sleep(2)
        return x + y
    
    
    print(b(5, 6))
    print('~' * 60)
    print(b(5, 6))  # immediate
    
    print(b(5.0, 6))  # immediate  11
    import functools, time
    
    
    @functools.lru_cache(maxsize=128, typed=True)
    def b(x, y, z=3):
        time.sleep(2)
        return x + y
    
    
    print(b(5, 6))
    print('~' * 60)
    print(b(5, 6))  # immediate
    
    print(b(5.0, 6))  # not immediate 11.0
    import functools, time
    
    
    @functools.lru_cache(maxsize=128, typed=True)
    def b(x, y=6, z=3):
        time.sleep(2)
        return x + y
    
    
    print(b(5, 6))
    print('~' * 60)
    print(b(5))  # not immediate
    print('~' * 60)
    print(b(5, y=6))  # not immedate

     

     

     

     

     

     

     

     

     

     

     

     

     

    import functools, time
    
    
    @functools.lru_cache(maxsize=None, typed=False)
    def b(x, y=6, z=3):
        time.sleep(2)
        return x + y
    
    
    print(b(x=5, y=6))
    print('~' * 60)
    print(b(x=5, y=6))  # immediate
    print('~' * 60)
    print(b(y=6, x=5))  # not immediate 没有对kwargs进行sorted
    
    
    @functools.lru_cache(maxsize=None, typed=True)
    def b(x):
        return x
    
    
    print(b([1, 2]))  # TypeError: unhashable type: 'list'

     

     

     

    import functools
    
    
    @functools.lru_cache(maxsize=None, typed=False)  # 无上限缓存
    def fibonacci(n):
        if n < 0:
            return None
        elif n < 2:
            return n
        else:
            return fibonacci(n - 1) + fibonacci(n - 2)
    
    
    print([fibonacci(v) for v in range(40)])
    
    print(fibonacci.cache_info())
    print(fibonacci.__wrapped__)
    print(fibonacci)
    fibonacci.cache_clear()
    print(fibonacci.cache_info())

     

     

     

     

     

     

     

     

     

     

     

     

     

  2. lru_cache实现

     

     

    from functools import wraps
    import time, inspect
    
    
    def cache(f):
        local_cache = dict()
    
        @wraps(f)
        def wrapper(*args, **kwargs):
            print(f'args: {args}, kwargs: {kwargs}')
    
            valor = dict()
            parameters = inspect.signature(f).parameters  # OrderedDict
            keys = list(parameters.keys())
    
            # positional arguments
            # 遇到VAR_POSITIONAL, 不能依次赋值了
            for i, arg in enumerate(args):
                print(parameters[keys[i]].kind)
                valor[keys[i]] = arg
    
            # keyword arguments
            # for k, v in kwargs.items():
            #     valor.setdefault(k, v)
            valor.update(kwargs)
    
            key = tuple(sorted(valor.items()))
            print(f'key: {key}')
    
            if key in local_cache.keys():
                return local_cache[key]
            else:
                ret = f(*args, **kwargs)
                local_cache[key] = ret
    
                return ret
    
        return wrapper
    
    
    @cache
    def b(x, y=55):
        time.sleep(2)
        return x + y
    
    
    # print(b(5, 55))
    # print(b(5, 55))
    # print(b(y=55, x=5))
    # print(b(5))
    
    @cache
    def p(a, b: int = 5, *args, c=55, d, **kwags):
        return a + b
    
    
    p(1, 2, 3, 4, 5, d='dd')

     

     

     

     

    from functools import wraps
    import time, inspect, datetime
    
    
    def duration(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            commence = datetime.datetime.now()
            ret = f(*args, **kwargs)
            delta = (datetime.datetime.now() - commence).total_seconds()
            print(f'\nfunction {f.__qualname__} elapsed {delta} seconds\n')
            return ret
    
        return wrapper
    
    
    def cache(duration = 3):
        local_cache = dict()
    
        def outer(f):
            @wraps(f)
            def wrapper(*args, **kwargs):
                print(f'args: {args}, kwargs: {kwargs}')
    
                def clear_expire_keys(cache: dict):
                    # perish expire keys
                    expire_keys = []
                    for k, (_, timestamp) in cache.items():
                        if datetime.datetime.now().timestamp() - timestamp > duration:
                            expire_keys.append(k)
    
                    for k in expire_keys:
                        cache.pop(k)
    
                clear_expire_keys(local_cache)
    
                def make_key(f, *args, **kwargs):
                    valor = dict()
                    parameters = inspect.signature(f).parameters  # OrderedDict
                    keys = list(parameters.keys())
                    print(f'keys: {keys}')
    
                    # positional arguments
                    # 遇到VAR_POSITIONAL, 不能依次赋值了
                    for i, v in enumerate(args):
                        # print(parameters[keys[i]].kind, type(str(parameters[keys[i]].kind)))
                        if parameters[keys[i]].kind.name == 'VAR_POSITIONAL':  # 判断是否为VAR_POSITIONAL
                            valor.setdefault(keys[i], tuple())
                            valor[keys[i]] += args[i:]
                            break
                        valor[keys[i]] = v
                        keys.remove(keys[i])  # 移除已经加入valor的参数
    
                    # keyword arguments
                    # valor.update(kwargs)  # 会产生参数覆盖问题
                    for k, v in kwargs.items():
                        if k in valor:
                            raise TypeError(f'{f.__qualname__}() got multiple values for argument {repr(k)}')
                        valor.update({k: v})
                        keys.remove(k)  # 移除已经加入valor的参数
    
                    # 在计算key前, 把未传递的默认值参数加入到valor, args, kwargs一定没有默认值
                    print(f'keys: {keys}')
                    for item in keys:
                        if parameters[item].default != parameters[item].empty:
                            valor.update({item: parameters[item].default})
                    return tuple(sorted(valor.items()))
    
                key = make_key(f, *args, **kwargs)  # 计算local_cache的 key
                print(f'key: {key}')
    
                if key not in local_cache.keys():
                    local_cache[key] = f(*args, **kwargs), datetime.datetime.now().timestamp()
    
                return local_cache[key]
    
                # if key in local_cache.keys():
                #     return local_cache[key]
                # else:
                #     ret = f(*args, **kwargs)
                #     local_cache[key] = ret
                #
                #     return ret
    
            return wrapper
    
        return outer
    
    
    @duration
    @cache()
    def b(x, y = 55):
        time.sleep(2)
        return x + y
    
    
    # print(b(5, 55))
    # print(b(5, 55, x=5, y=55))  # valor.update(kwargs), b(5, 55) 调用后, local_cache缓存 key: (('x', 5), ('y', 55)), 直接返回, 不会传参给原函数, 在对kwargs合并时, 做检查
    # print(b(5, 55, x=55, y=55))  # local_cache 找不到 key: (('x', 55), ('y', 55)) 时, 会传参给原函数, 异常
    # print(b(5, y=55))
    # print(b(5))
    
    
    # print(b(5, 55))
    # print(b(y=55, x=5))
    # print(b(5))
    
    @duration
    @cache(duration = 4)
    def p(a, b: int = 5, *args, c = 55, d, **kwargs):
        time.sleep(2)
        return a + b
    
    
    print(p(1, d = 'dd'))
    time.sleep(3.5)
    print(p(1, b = 5, d = 'dd'))
    print(p(1, b = 5, c = 55, d = 'dd'))
    # print(p(1, 2, 3, 4, 5, d='dd'))

     

     

     

posted @ 2022-02-27 23:40  ascertain  阅读(39)  评论(0编辑  收藏  举报