python-functools

python-functools

functools模块用于高阶函数:作用与或者返回其它函数的函数。一般来说,对于该模块,任何可调用对象都可以视为一个函数

partial偏函数

偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一个新的函数并返回
从partial生成的新函数, 并且可以像原始对象一样对待,这样可以简化函数调用。

partial(func,*args,**keywords)
仅仅作用函数,输入 函数,函数的参数,返回函数对象
主要作用:减少可调用对象的参数个数
## 等价于
def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

案例1-普通使用

import functools
def add(x, y) -> int:
    return x + y

newadd = functools.partial(add, y=5)

print(newadd(7))     	  # 12
print(newadd(7, y=6)) 	  # 13
print(newadd(y=10, x=6))  # 16

可以在 json_serial_fallback 函数中添加类型判断来指定如何 json 序列化一个 Python 对象

def json_serial_fallback(obj):
    """
    JSON serializer for objects not serializable by default json code
    """
    if isinstance(obj, (datetime.datetime, datetime.date)):
        return str(obj)
    if isinstance(obj, bytes):
        return obj.decode("utf-8")
    raise TypeError ("%s is not JSON serializable" % obj)

json_dumps = partial(json.dumps, default=json_serial_fallback)

案例2-延迟执行

import time
import functools

class DelayFunc:
  def __init__(self, duration, func):
    self.duration = duration
    self.func = func

  def __call__(self, *args, **kwargs):
    print(f'Wait for {self.duration} seconds...')
    time.sleep(self.duration)
    return self.func(*args, **kwargs)

  def eager_call(self, *args, **kwargs):
    print('Call without delay')
    return self.func(*args, **kwargs)

def delay(duration):
  """
  装饰器:推迟某个函数的执行。
  同时提供 .eager_call 方法立即执行
  """
  # 此处为了避免定义额外函数,
  # 直接使用 functools.partial 帮助构造 DelayFunc 实例
  return functools.partial(DelayFunc, duration)

@delay(duration=2)
def add(a, b):
  return a+b

print(add(3,5))
# Wait for 2 seconds...
# 8

案例3-回调函数添加参数

partial() 通常被用来微调其他库函数所使用的回调函数的参数

def output_result(result, log=None):
    if log is not None:
        log.debug('Got: %r', result)
# A sample function
def add(x, y):
    return x + y
if __name__ == '__main__':
    import logging
    from multiprocessing import Pool
    from functools import partial
    logging.basicConfig(level=logging.DEBUG)
    log = logging.getLogger('test') p = Pool()
    p.apply_async(add, (3, 4), callback=partial(output_result, log=log))
    p.close()
    p.join()

partialmethod

partialmethod  仅作用于方法
其行为类似 partial但它被设计用作方法定义而非直接用作可调用对象。
class Cell(object):
    def __init__(self):
        self._alive = False
    @property
    def alive(self):
        return self._alive
    def set_state(self, state):
        self._alive = bool(state)

    set_alive = partialmethod(set_state, True)
    set_dead = partialmethod(set_state, False)

c = Cell()
c.alive         # False
c.set_alive()
c.alive         # True

reduce

该函数的作用是将一个序列归纳为一个输出。

reduce(function, iterable[, initializer])

# 大致等于
def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value
from functools import reduce

def foo(x, y):
    return x + y

l = range(1, 10)
res=reduce(foo, l)
print(res)  # 45

res2=reduce(foo, l, 10)   # 初始值=10
print(res2) # 55

wraps

任何时候你定义装饰器的时候,都应该使用 functools 库中的 @wraps 装饰器来注解底层包装函数

wraps 装饰器能消除装饰器带来的副作用

from functools import wraps

def decorator(func):
    # @wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper func"""
        return func(*args, **kwargs)
    return wrapper

@decorator
def add(x, y):
    """add func"""
    return x + y


print(add.__name__)  # wrapper
print(add.__doc__)    # wrapper func
print(add)   # <function __main__.wrapper>
print(add(1,2))   

# 函数的 __name__ 属性变成了 wrapper ,实际上add 函数整个变成了 decorator(add)
# 这就是装饰器函数的副作用

增加 @wrap 修饰,消除装饰器的副作用

from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper func"""
        return func(*args, **kwargs)
    return wrapper

@decorator
def add(x, y):
    """add func"""
    return x + y

print(add.__name__)  # add
print(add.__doc__)    # add func

print(add)   # <function __main__.add>
print(add(1,2))   

update_wrapper

update_wrapper 的作用与 wraps 类似,不过功能更加强大,换句话说,wraps 其实是 update_wrapper 的特殊化

实际上 wraps(wrapped) 相当于 partial(update_wrapper, wrapped=wrapped, **kwargs)

cache(user_function)

简单轻量级未绑定函数缓存

返回值与 lru_cache(maxsize=None) 相同,创建一个查找函数参数的字典的简单包装器。 因为它不需要移出旧值,所以比带有大小限制的 lru_cache()更小更快。

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1
    
 factorial(10)   # 3628800
 factorial(12)   # 479001600

lru_cache(maxsize)

LRU缓存,一个为函数提供缓存功能的装饰器,缓存 maxsize 组传入参数,在下次以相同参数调用时直接返回上一次的结果。用以节约高开销或I/O函数的调用时间。可以减少频繁调用相同的函数使用相同参数的I/O消耗。提升递归函数的执行效率

由于使用字典来缓存结果,因此传给该函数的位置和关键字参数必须为hashable

使用前提
    同样的函数参数一定得到同样的结果
    函数执行时间很长,且要多次执行
本质是函数调用的参数=>返回值
# 缓存同一个函数 相同参数执行的结果
@lru_cache(maxsize=128)
def work(n):
    if n == 1:
        return 1
    else:
        return work(n - 1) * n

print(work(6))
720

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)
fiblist=[fib(n) for n in range(16)]
print(fiblist)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

singledispatch

要定义一个泛型函数,用装饰器 @singledispatch 来装饰它,函数根据传参类型不同,做出不同的操作.

from functools import singledispatch
@singledispatch
def typecheck(a):
    print(a, type(a), 'a')


@typecheck.register(str)
def _(text):
    print(text, type(text), 'str')


@typecheck.register(list)
def _(text):
    print(text, type(text), 'list')

@typecheck.register(int)
def _(text):
    print(text, type(text), 'int')

if __name__ == '__main__':
    typecheck([1,2,3,4])

 # [1, 2, 3, 4] <class 'list'> list

singledispatchmethod

class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg

参考资料

https://docs.python.org/zh-cn/3.10/library/functools.html?highlight=partial#

http://juzizhou.net/article/322

https://blog.csdn.net/xhtchina/article/details/128487830

posted @ 2023-10-18 18:17  贝壳里的星海  阅读(29)  评论(0编辑  收藏  举报