• Python 模块简介 -- functools


    Python 的 functools 模块可以说主要是为函数式编程而设计,用于增强函数功能。

    functools.partial

    用于创建一个偏函数,它用一些默认参数包装一个可调用对象,返回结果是可调用对象,并且可以像原始对象一样对待,这样可以简化函数调用。实际上 partial 相当于一个高阶函数,其大致的实现如下(实际在标准库中它是用 C 实现的):

    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
    

    一个简单的使用示例:

    from functools import partial
    
    def add(x, y):
        return x + y
    
    add_y = partial(add, 3)  # add_y 是一个新的函数
    add_y(4)
    

    一个很实用的例子:

    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)
    

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

    functools.update_wrapper

    用 partial 包装的函数是没有 __name____doc__,这使得依赖于这些属性的代码可能无法正常工作。update_wrapper 可以拷贝被包装函数的 __name____module____doc____dict__属性到新的封装函数中去,其实现也非常简单:

    WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
    WRAPPER_UPDATES = ('__dict__',)
    def update_wrapper(wrapper,
                       wrapped,
                       assigned = WRAPPER_ASSIGNMENTS,
                       updated = WRAPPER_UPDATES):
    
        for attr in assigned:
            setattr(wrapper, attr, getattr(wrapped, attr))
        for attr in updated:
            getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    
        return wrapper
    

    update_wrapper 主要用在装饰器函数中,以确保被装饰的保留原理的属性。示例:

    def wrap(func):
        def call_it(*args, **kwargs):
            print "calling", func.__name__
            return func(*args, **kwargs)
        return call_it
    
    @wrap
    def hello():
        print "hello"
    
    from functools import update_wrapper
    def wrap2(func):
        def call_it(*args, **kwargs):
            print "calling", func.__name__
            return func(*args, **kwargs)
        return update_wrapper(call_it, func)
    
    @wrap2
    def hello2():
        print "hello2"
    
    
    # Script starts from here
    
    if __name__ == '__main__':
        print hello.__name__  # call_it
        print hello2.__name__  # hello2
    

    functool.wraps

    wraps 函数是为了在装饰器中方便的拷贝被装饰函数的签名,而对 update_wrapper 做的一个包装,其实现如下:

    def wraps(wrapped,
              assigned = WRAPPER_ASSIGNMENTS,
              updated = WRAPPER_UPDATES):
    
        return partial(update_wrapper, wrapped=wrapped,
                       assigned=assigned, updated=updated)
    

    示例:

    from functools import wraps
    def wrap3(func):
        @wraps(func)
        def call_it(*args, **kwargs):
            print "calling", func.__name__
            return func(*args, **kwargs)
        return call_it
    
    @wrap3
    def hello3(func):
        print "hello3"
    
    print hello3.__name__  # hello3
    

    functools.reduce

    在 Python2 中等同于内建函数 reduce,但是在 Python3 中内建的 reduce 函数被移除,只能使用 functools.reduce。该函数的作用是将一个序列归纳为一个输出,其原型如下:

    reduce(function, sequence, startValue)
    

    使用示例:

    >>> def foo(x, y):
    ...     return x + y
    ...
    >>> l = range(1, 10)
    >>> reduce(foo, l)
    45
    >>> reduce(foo, l, 10)
    55
    

    functools.cmp_to_key

    在 list.sort 和 内建函数 sorted 中都有一个 key 参数,这个参数用来指定取元素的什么值进行比较,例如按字符串元素的长度进行比较:

    >>> x = ['hello','abc','iplaypython.com']
    >>> x.sort(key=len)
    >>> x
    ['abc', 'hello', 'iplaypython.com']
    

    也就是说排序时会先对每个元素调用 key 所指定的函数,然后再排序。同时,sorted 和 list.sort 还提供了 cmp 参数来指定如何比较两个元素,但是在 Python 3 中该参数被去掉了。cmp_to_key 函数就是用来将老式的比较函数转化为 key 函数。用到 key 参数的函数还有 sorted(), min(), max(), heapq.nlargest(), itertools.groupby() 等。

    functools.total_ordering

    这个一个类装饰器,如果一个类实现了 __lt____le____gt____ge__ 这些方法中的至少一个,该装饰器会自动实现其他几个方法。示例:

    from functools import total_ordering
    @total_ordering
    class Student:
        def __init__(self, firstname, lastname):
            self.firstname = firstname
            self.lastname = lastname
    
        def __eq__(self, other):
            return ((self.lastname.lower(), self.firstname.lower()) ==
                    (other.lastname.lower(), other.firstname.lower()))
    
        def __lt__(self, other):
            return ((self.lastname.lower(), self.firstname.lower()) <
                    (other.lastname.lower(), other.firstname.lower()))
    
    print dir(Student)
    
    stu = Student("Huoty", "Kong")
    stu2 = Student("Huoty", "Kong")
    stu3 = Student("Qing", "Lu")
    
    print stu == stu2
    print stu > stu3
    

    输出结果:

    ['__doc__', '__eq__', '__ge__', '__gt__', '__init__', '__le__', '__lt__', '__module__']
    True
    False
    

    functools.lru_cache

    这个装饰器是在 Python3 中新加的,在 Python2 中如果想要使用可以安装第三方库 functools32。该装饰器用于缓存函数的调用结果,对于需要多次调用的函数,而且每次调用参数都相同,则可以用该装饰器缓存调用结果,从而加快程序运行。示例:

    from functools import lru_cache
    @lru_cache(None)
    def add(x, y):
        print("calculating: %s + %s" % (x, y))
        return x + y
    
    print(add(1, 2))
    print(add(1, 2))  # 直接返回缓存信息
    print(add(2, 3))
    

    输出结果:

    calculating: 1 + 2
    3
    3
    calculating: 2 + 3
    5
    

    由于该装饰器会将不同的调用结果缓存在内存中,因此需要注意内存占用问题,避免占用过多内存,从而影响系统性能。

    functools.singledispatch

    单分发器,Python 3.4 新增,用于实现泛型函数,由一个单一参数的类型来决定调用哪个函数。示例:

    @singledispatch
    def fun(arg, verbose=False):
        if verbose:
            print("Let me just say", end=" ")
        print(arg)
    
    
    @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)
    
    
    fun("Hello world.")
    fun(18)
    fun(["a", "b"])
    

    输出:

    Hello world.
    18
    0 a
    1 b
    

    一个还可以支持多个泛型:

    @fun.register(float)
    @fun.register(Decimal)
    def _(arg, verbose=False):
        print(arg)
    

    fun.registry 中保存了所有的泛型函数,使用 fun.registry[float] 或者 fun.dispatch(float) 可以获取相应的函数:

    >>> fun.registry
    mappingproxy({int: <function singledispatch._>,
                  float: <function singledispatch._>,
                  list: <function singledispatch._>,
                  object: <function singledispatch.fun>,
                  decimal.Decimal: <function singledispatch._>})
    >>> fun.dispatch(float)
    <function singledispatch._>
    >>> fun.registry[int]
    <function singledispatch._>
    
  • 相关阅读:
    决定Java程序员工资高低的三个因素
    Java常见面试题汇总(七)
    Java的 RMI与RPC的区别
    2019新版Java学习路径
    JAVA中String,StringBuilder以及StringBuffer三个类之间的区别
    2019Java开发学习路线图
    bzoj1458 士兵占领
    bzoj1303 [CQOI2009]中位数图
    bzoj3721 [PA2014 Final] Bazarek
    bzoj2659 [Beijing wc2012]算不出的算式
  • 原文地址:https://www.cnblogs.com/paisenpython/p/10339481.html
Copyright © 2020-2023  润新知