Python的functools模块

发布时间:2019-08-27 08:03:04编辑:auto阅读(1385)

    (wrapperwrappedassigned = WRAPPER_ASSIGNMENTSupdated = WRAPPER_UPDATES):

      类似咱们自己写的copy_preperties功能;

      wrapper包装函数, wrapped被包装函数;

      元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性:模块名、名称、限定名、文档、参数注解

    WRAPPER_ASSIGNMENTS = ()

      元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典:  

    WRAPPER_UPDATES = ('__dict__',)

      增加一个__wrapped__属性,保留着wrapped函数;


    import datetime, time, functools
    
    def logger(fn):
        # @copy_properties(fn)
        def wrap(*args, **kwargs):
            """This is a wrapper"""
            #before 功能增强
            print("args={},kwargs={}".format(args, kwargs))
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            #after 功能增强
            duration = datetime.datetime.now() - start
            print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
            return ret
        functools.update_wrapper(wrap, fn)# 这里就等价于@copy_properties(fn)
        print("{} {}".format(id(wrap), id(fn)))# 这里是为了确认一下,add.__wrapped__到底是wrap,还是fn
        return wrap
    
    @logger
    def add(x, y):
        """This is a function"""
        print("======call add======")
        time.sleep(2)
        return x + y
    
    print(add.__wrapped__)
    print(id(add.__wrapped__))

    其实按照源码来写的话,return wrap 可以直接写成return functools.update_wrapper(wrap, fn),为什么直接能这样写,自己看源码?



    上面的方法,我们很少这样用,我们真正要用的是@functools.wraps(fn)

    import datetime, time, functools
    
    def logger(fn):
        @functools.wraps(fn)
        def wrap(*args, **kwargs):
            """This is a wrapper"""
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            duration = datetime.datetime.now() - start
            print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
            return ret
        return wrap
    
    @logger# add = logger(add)
    def add(x, y):
        """This is a function"""
        print("======call add======")
        time.sleep(2)
        return x + y
    
    print(add.__name__, add.__wrapped__)



    partial方法

      偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一个新的函数并返回;

      从partial生成的新函数,是对原函数的封装;

    举例说明:

    import functools
    import inspect
    
    def add(x, y) -> int:
        return x + y
    print(inspect.signature(add)) #(x, y) -> int
    newadd = functools.partial(add, y=5)
    
    print(newadd(5))
    print(newadd(5)) #TypeError: add() got multiple values for argument 'y'
    print(newadd(7, y=6))
    print(newadd(y=6, x=10))
    
    print(inspect.signature(newadd)) #这里是看一下新函数的签名,(x, *, y=5) -> int

    更复杂一点:

    import functools
    import inspect
    
    def add(x, y, *args) ->int:
        print(args)
        return x + y
    
    newadd = functools.partial(add, 1, 3, 6, 5)
    print(newadd(7))
    print(newadd(7, 10))
    # print(newadd(7, 10, y=20, x=10))###
    print(newadd())
    
    print(inspect.signature(newadd))



    lru_cache:

      @functools.lru_cache(maxsize=128, typed=False)

      least-recently-used装饰器。lru,最近最少使用,cache缓存

      如果maxsize设置为None,则禁用LRU功能,并且缓存可以当无限制增长。

      当maxsize是二的幂时,LRU功能执行得最好。

      如果typed设置为True,则不同类型的函数参数将单独缓存。例如,f(3)和f(3.0)将被视为具有不同结果的不同调用。

    import functools, time
    
    @functools.lru_cache()
    def add(x, y, z=3):
        time.sleep(z)
        return x + y
    
    print(add(4, 5))
    print(add(4, 5))
    print(add(4, 5))
    print(add(4, 5))
    
    #在ipython里看的效果会更清楚


关键字