Python的functools模块

2023-01-31 02:01:27 python functools 模块
(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里看的效果会更清楚


相关文章