• python functools模块


    原文:https://www.jianshu.com/p/178788297a5c

    1 functools函数

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

    functools模块定义了以下函数:

    1.1 functools.cmp_to_key(func)

    版本3.2中新增。
    

    将旧风格的比较函数转换为key函数。用于接收key函数的工具(例如sorted()min()max()heapq.nlargest()heapq.nsmallest()itertools.groupby())。该函数主要用作支持比较函数的Python 2转换工具。

    比较函数可以是任何可调用的对象,接收两个参数,比较它们,如果小于返回负数,相等返回0,大于返回正数。key函数是一个可调用对象,接收一个参数,并返回另一个值用于排序的键。

    示例:

    sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order

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

    版本3.2中新增。
    版本3.3中修改:增加可选参数typed参数。
    

    装饰器用一个有记忆的调用包装一个函数,它可以保存最近maxsize次调用。当使用同样的参数定期调用费时或I/O绑定的函数时,它可以节省时间。

    因为使用字典缓存结果,所以函数的位置和关键字参数必须是hashable

    如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限增长。当maxsize设置为$ 2^n $时,性能最佳。

    如果typed设置为真,则不同类型的函数参数会分别缓存。例如,f(3)f(3.0)将视为不同结果的不同调用。

    为了帮助测量缓存的有效性并调整maxsize参数,包装函数使用cache_info()函数返回一个命名元组,包括hitsmissesmaxsizecurrsize。在多线程环境中,hitsmisses是近似值。

    装饰器还提供了cache_clear()函数用于清除缓存,或者让缓存失效。

    原始的底层函数通过wrapped属性访问。这对于内省,绕过缓存,或者重新装饰函数很有用。

    当最近调用是即将调用的最佳调用因子时(例如,新闻服务器上的最受欢迎文章常常每天改变),LRU(least recently used)缓存效果最好。缓存的大小限制确保缓存不会在长时间运行的进程(如web服务器)上不受限制的增长。

    用于静态Web内容的LRU缓存示例:

     1 @lru_cache(maxsize=32)
     2 def get_pep(num):
     3     'Retrieve text of a Python Enhancement Proposal'
     4     resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
     5     try:
     6         with urllib.request.urlopen(resource) as s:
     7             return s.read()
     8     except urllib.error.HTTPError:
     9         return 'Not Found'
    10         
    11 >>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
    12 ...     pep = get_pep(n)
    13 ...     print(n, len(pep))
    14 
    15 >>> get_pep.cache_info()
    16 CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

    使用缓存实现动态编程技术高效计算斐波那契数列的示例:

    @lru_cache(maxsize=None)
    def fib(n):
        if n < 2:
            return n
        return fib(n-1) + fib(n-2)
        
    >>> [fib(n) for n in range(16)]
    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
    
    >>> fib.cache_info()
    CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
    

    1.3 @functools.total_ordering

    版本3.2中新增。
    版本3.4中修改:如果是不能识别的类型,现在支持从底层比较函数返回NotImplemented。
    

    给定的一个类定义了一个或多个富比较方法,该类装饰器提供剩下的。这简化了指定所有富比较操作的工作量。

    类必须定义__lt__()__le__()__gt__()__ge__()的其中一个。此外,类应该提供一个__eq__()方法。

    示例:


    @total_ordering
    class Student:
        def _is_valid_operand(self, other):
            return (hasattr(other, 'lastname') and
                    hasattr(other, 'firstname'))
        def __eq__(self, other):
            if not self._is_valid_operand(other):
                return NotImplemented
            return ((self.lastname.lower(), self.firstname.lower()) ==
                    (other.lastname.lowher(), other.firstname.lower()))
        def __lt__(self, other):
            if not self._is_valid_operand(other):
                return NotImplemented
            return ((self.lastname.lower(), self.firstname.lower()) <
                    (other.lastname.lower(), other.firstname.lower()))

    注意:虽然该装饰器能很容易的创建行为良好的完全有序类型,但会导致衍生出的比较函数执行的更慢,以及更复杂的堆栈跟踪。如果性能基准测试表明这是程序的瓶颈,则实现所有六个富比较函数可能会是提高速度的方式。

    1.4 functools.partial(func, *args, **keywords)

    返回一个新的partial对象,该对象被调用时,类似使用位置参数args和关键字参数keywords调用func。如果调用时提供了更多参数,它们会被添加到args。如果提供了额外的关键字参数,它们会扩展和覆盖keywords。大致等价于:

    def partial(func, *args, **keywords):
        def newfunc(*fargs, **fkeywords):
            newkeywords = keywords.copy()
            newkeywords.update(fkeywords)
            return func(*args, *fargs, **newkeywords)
        newfunc.func = func
        newfunc.args = args
        newfunc.keywords = keywords
        return newfunc

    partial()用于冻结函数的某些参数和/或关键字参数,生成一个简化的签名对象。例如,用于创建一个类似int()函数的可调用对象,其中base参数默认为2:

    >>> from functools import partial
    >>> basetwo = partial(int, base=2)
    >>> basetwo.__doc__ = 'Convert base 2 string to an int.'
    >>> basetwo('10010')
    18

    1.5 类方法 functools.partialmethod(func, *args, **keywords)

    版本3.4中新增。
    

    返回一个行为类似partial的新partialmethod描述符,除了它是用于方法定义,而不是直接调用。

    func必须是一个descriptor或者可调用对象(两个对象都像常规函数一样作为descriptor)。

    func是一个descriptor(比如普遍的Python函数,classmethod()staticmethod()abstractmethod(),或者其它partialmethod实例时,get的调用会委托给底层的descriptor,并返回一个适当的partial对象。

    func不是可调用的descriptor时,会动态创建一个适当的绑定方法。用于方法时,该行为类似普通的Python函数:self参数会插入为第一个位置参数,甚至在传递给partialmethod构造器的argskeywords之前。

    示例:


    >>> 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

    1.6 functools.reduce(function, iterable[, initializer])

    将两个参数的function从左至右依次作用于序列中的项,减少序列为单个值。例如,reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])会计算((((1+2)+3)+4)+5)。左边的参数x是计算出的值,右边的参数y是从序列中更新的值。如果提供了可选参数initializer,在计算中,它会放在序列项之前,当序列为空时,提供一个默认值。如果没指定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

    1.7 @functools.singledispatch(default)

    版本3.4中新增。
    

    将函数转换为single-dispatch generic函数。

    使用@singledispatch装饰器定义generic函数。注意,dispatch发生在第一个参数的类型上,相应的创建函数:


    >>> from functools import singledispatch
    >>> @singledispatch
    ... def fun(arg, verbose=False):
    ...     if verbose:
    ...         print('Let me juset say,', end=' ')
    ...     print(arg)

    使用generic函数的register()属性添加函数的重载实现。这是一个装饰器,接受一个类型参数,并装饰实现该类型操作的函数:

    >>> @fun.register(int)
    ... def _(arg, verbose=False):
    ...    if verbose:
    ...        print('Strength in numbers, eh?', end=' ')
    ...    print(arg)
    ...
    >>> @fun.register(list)
    ... def _(arg, verbose=False):
    ...     if verbose:
    ...         print('Enumerate this:')
    ...     for i, elem in enumerate(arg):
    ...         print(i, elem)

    为了能够注册lambda表达式和预先存在的函数,register()可以用于函数形式:

    >>> def nothing(arg, verbose=False):
    ...     print('Nothing.')
    ...
    >>> fun.register(type(None), nothind)

    register()属性返回未装饰的函数,可以使用装饰堆叠,pickling,以及为每个变体单独创建单元测试:

    >>> @fun.register(float)
    ... @fun.register(Decimal)
    ... def fun_num(arg, verbose=False):
    ...     if verbose:
    ...         print('Half of your number:', end=' ')
    ...     print(arg / 2)
    ...
    >>> fun_num is fun
    False

    调用时,generic函数根据第一个参数的类型dispatch

    >>> fun('Hello World.')
    Hello World.
    >>> fun('test.', verbose=True)
    Let me just say, test.
    >>> fun(42, verbose=True)
    Strength in numbers, eh? 42
    >>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
    Enumerate this:
    0 spam
    1 spam
    2 eggs
    3 spam
    >>> fun(None)
    Nothing.
    >>> fun(1.23)
    0.615

    当没有注册特定类型的实现时,其方法解析顺序用于查找更通用的实现。用@singledispatch装饰的原始函数是为object类型注册的,如果没有找到更好的实现,则使用它。

    使用dispatch()属性查看generic函数为指定类型选择哪个实现:

    >>> fun.dispatch(float)
    <function fun_num at 0x1035a2840>
    >>> fun.dispatch(dict)
    <function fun at 0x103fe0000>

    使用只读属性registry访问所有注册的实现:

    >>> fun.registry.keys()
    dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
              <class 'decimal.Decimal'>, <class 'list'>,
              <class 'float'>])
    >>> fun.registry[float]
    <function fun_num at 0x1035a2840>
    >>> fun.registry[object]
    <function fun at 0x103fe0000>

    1.8 functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

    版本3.2中新增:自动添加__wrapped__属性。
    版本3.2中新增:默认拷贝__annotations__属性。
    版本3.2中修改:缺少属性不再触发AttributeError。
    版本3.4中修改:现在__wrapped__属性总是引用wrapped函数,即使该属性定义了__wrapped__属性。
    

    更新一个wrapper函数,让它看起来像一个wrapped函数。可选参数是元组,用来指定原函数的哪些属性直接分配给wrapper函数的匹配属性,wrapper函数的哪些属性从原函数的相应属性更新。这些参数的默认值是模块级别的常量WRAPPER_ASSIGNMENTS(分配wrapper函数的__module____name____qualname____annotations__和文档字符串__doc__)和WRAPPER_UPDATES(哪些更新wrapper函数的__dict__,比如实例的字典)。

    为了内省和其它目的(比如绕过缓存装饰器lru_cache())允许访问原始函数,该函数自动在wrapper函数添加一个__wrapped__属性,应用wrapped函数。

    该函数的主要用途是,在装饰器函数中包装被装饰的函数,并返回wrapper。如果wrapper函数没有更新,返回函数的元数据将反射wrapper的定义,而不是原始函数的定义,原始函数的定义通常没有意义。

    除了函数,update_wrapper()可以与其它可调用对象一起使用。出现在assignedupdated参数中,而不在被包装对象中的任何属性都会被忽略(比如,该函数不会试图在wrapper函数中设置它们)。如果wrapper函数本身缺少updated中的属性,仍然会抛出AttributeError

    1.9 @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

    这是一个便捷函数,定义一个wrapper函数时,作为函数装饰器调用update_wrapper()。它等价于partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)。例如:


    >>> from functools import wraps
    >>> def my_decorator(f):
    ...     @wraps(f)
    ...     def wrapper(*args, **kwds):
    ...         print('Calling decorated function')
    ...         return f(*args, **kwds)
    ...     return wrapper
    ...
    >>> @my_decorator
    ... def example():
    ...     """Docstring"""
    ...     print('Called example function')
    ...
    >>> example()
    Calling decorated function
    Called example function
    >>> example.__name__
    'example'
    >>> example.__doc__
    'Docstring'

    没有使用这个装饰工厂,example函数的名称将是'wrapper',并且原始example()docstring将会丢失。

    2 partial对象

    partial()创建可调用的partial对象。它们有三个只读属性:

    2.1 partial.func

    一个可调用的对象或函数。调用partial对象会转为使用新的参数和关键字参数调用func

    2.2 partial.args

    最左边的位置参数会优先作为位置参数提供给partial对象调用。

    2.3 partial.keywords

    partial对象被调用时提供关键字参数。

    partial对象与函数对象类似,它们可以被调用,有弱引用,并且可以有属性。但有一些重要的区别。对于实例,__name____doc__属性不会自动创建。同时,在类中定义的partial对象的行为类似静态方法,在实例属性查找时,不会转换为绑定方法。

  • 相关阅读:
    通过 SingleFlight 模式学习 Go 并发编程
    进程内优雅管理多个服务
    20192406梁健 202120222 《网络与系统攻防技术》实验四实验报告
    20192406梁健 202120222 《网络与系统攻防技术》实验五实验报告
    oracle存储过程迁移到PostgreSQL之问题总结
    frida 获取app里所有调用 java.lang.String的值
    firad 绕过ssl
    python frida 安装
    mitmproxy 夜神系统级证书
    MultiEntity AspectBased Sentiment Analysis with Context, Entity and Aspect Memory论文翻译
  • 原文地址:https://www.cnblogs.com/guolei2570/p/8845915.html
Copyright © 2020-2023  润新知