python高阶函数、柯里化、装饰器、参数注解

南楼画角 提交于 2019-11-28 01:49:32

一、高阶函数

  •  函数在python中是一等公民
  •  函数也是对象,可调用的对象
  •  函数可以作为普通变量,参数,返回值等等
  •  高阶函数应当是至少满足:接受一个或多个函数作为参数;输出是一个函数
举例:    
    def counter(base):
        def inc(step=1)
            base += step
            return base
        return inc


1、自定义sort函数

 思路:

  • 仿照内建函数sorted,自行实现一个sort函数(不使用内建函数),能够为列表元素排序
  • 内建函数sorted函数是返回一个新的列表,可以设置升序或者降序,可以设置一个排序的函数
  • sorted函数的实现原理,扩展到map,filter函数的实现原理
方式一:
        def sort(iterable,reverse=False):
            ret = []
            for x in iterable:
                for i,y in enumerate(ret):
                    flag = x>y if reverse else x<y
                    if flag:   #找到大的就地插入
                        ret.insert(i,x)
                        break
                else:
                    ret.append(x)
            return ret
        print(sort([1,3,4,52,6,10])
    
    
    方式二:
        def sort(iterable,fn=lambda a,b:a>b):
            ret = []
            for x in iterable:
                for i,y in enumerate(ret):
                    
                    if fn(x,y):   #fn函数的返回值是bool
                        ret.insert(i,x)
                        break
                else:
                    ret.append(x)
            return ret
        print(sort([1,3,4,52,6,10])

 

2、sorted(iterable[,key][,reverse]) 排序函数

  • 返回一个新的列表,对一个可迭代对象的所有元素排序,排序规则为key定义的函数,reverse表示排序反向
    sorted(lst,key=lambda x:6-x) 排序返回一个新列表
    >>> lst = [1,3,4,52,6,10]
    >>> sorted(lst,key=lambda x:6-x)
    [52, 10, 6, 4, 3, 1]
    
    list.sort(key=lambda x:6-x)  就地修改lst,没有返回一个新列表
    >>> lst.sort(key=lambda x:6-x)
    >>> lst
    [52, 10, 6, 4, 3, 1]
    >>> 

 

3、filter(function,iterable)

  •  过滤可迭代对象的元素,返回一个迭代器
  •  function一个具有一个参数的函数,返回bool
    举例:过滤出数列中能被3整除的数字
        >>> lst = filter(lambda x:x%3==0,[1,9,55,150,-3,78,28,123])
        >>> list(lst)
        [9, 150, -3, 78, 123]
        >>> 

 

4、map(function,*iterables) --> map object

  • 对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器
    >>> list(map(lambda x:2*x+1,range(5)))
    [1, 3, 5, 7, 9]
    >>> 
    
    >>> dict(map(lambda x:(x%5,x),range(500)))
    {0: 495, 1: 496, 2: 497, 3: 498, 4: 499}
    >>> 


二、柯里化

  •  指的是将原来接收的两个参数的函数变成新的接收一个参数的函数的过程,新的函数返回一个以原有的第二个参数为函数参数
  •  通过嵌套函数就可以把函数转换成柯里化函数
    如:z = f(x,y)z转换成z = f(x)(y)的形式
    将加法函数柯里化
        def add(x,y):
            return x + y
    转换如下:
        def add(x):
            def_add(y)
                return x + y
            return _add
        add(5)(6)


三、装饰器

1、无参装饰器

  •   它是一个函数、函数作为他的形参
  •   返回值也是一个函数,可以使用@function name方式简化调用
  •   装饰器做到了业务功能分离,但是fn函数调用传参是个问题
        @logger  # 等价于add = logger(add)
        def add(x,y):  #业务函数
            return x+y
        
        def logger(fn):  #装饰器函数
            def wrapper(*args,**kwargs):
                print('begin')
                x = fn(*args,**kwargs)
                print('end')
                return x
            return wrapper
        print(logger(add)(5,y=50))  等价于print(add(45,40))
        


2、装饰器和高阶函数

  • 装饰器是高阶函数,但是装饰器是对传入函数的功能的装饰(功能增强)
举例:
            import datatime
            import time
            def logger(fn):
                def wrap(*args,**kwargs):
                    #befor 功能增强
                    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
                return wrap
                
            @logger 
            def add(x,y):
                print('==========call add===========')
                time.sleep(2)
            print(add(4,y=7))
            

 

3、文档字符串

  •  python的是文档字符串Documentation Strings
  •  在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
  •  惯例是首字母大写
  •  可以使用特殊熟悉__doc__访问这个文档
    举例:
        >>> def add(x,y):
            """This is a function addition"""
            a = x+y
            return x+y

        >>> print("name={} doc={}".format(add.__name__,add.__doc__))
        name=add doc=This is a function addition
        >>> print("name={}\ndoc={}".format(add.__name__,add.__doc__))
        name=add
        doc=This is a function addition
        >>>     


4、带参装饰器

  •  通过属性函数将被包装函数的属性覆盖掉包装函数
  •  凡是被装饰的函数都需要复制这些属性,这个函数很通用
  •  可以将复制属性的函数构建成装饰器函数,带参装饰器
1, 提供一个函数,被封装函数属性==copy==》包装函数属性,改造成带参装饰器
    def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dsr.__doc__ = src.__doc__
        return dst
    return _copy

    def logger(fn):
        @copy_properties(fn)  # wrapper = wrapper(fn)(wrapper)
        def wrapper(*args,**kwargs):
            'I am wrapper'
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        return wrapper
    @logger
    def add(x,y):
        '''This is a function for add'''
        return x+y
    print("name={},doc={}".format(add.__name__,add.__doc__))

2,  需求:获取函数的执行时长,对时长超过阈值的函数记录一下
import time 
import datetime

def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst
    return _copy

def logger(duration):
    def _logger(fn):
        @copy_properties(fn)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            print(111,args,kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            print(2222,delta,duration)  # duration阈值设置为5秒,由logger函数传入
            print('so slow') if delta > duration else print('so fast')
            return ret
        return wrapper
    return _logger
@logger(5)  # add = logger(5)(add)
def add(x,y):
    time.sleep(3)
    return x+y

print(add(5,6))

 

改进装饰器logger函数

def logger(dutation,func=lambda name,dutation:print('{} took {}s'.format(name,dutation))):
    def _logger(fn):
        @copy_properties(fn)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return wrapper
    return _logger


四、functools 模块

  •  类似copy_properties功能
  •  functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENIS,updated=WEAPPER_UPDATES) 
  •  wrapper是包装函数,wrapped是被包装函数
  •  元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性:
  •  模块名(__module__),名称(__name__),限定名(__qualname__),文档(__doc__),参数注解(__annotations__)
  •  元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
  •  增加一个__wrapped__属性,保留着wrapped函数
import datetime,time,functools

def logger(duration,func=lambda name,duration:print('{}took {}s'.format(name,duration))):
     
    def _logger(fn):
        @functools.wraps(fn)
        def wrapper(*args,**kwargs):
            '''I am wrapper'''
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return wrapper
    return _logger
@logger(5)
def add(x,y):
    '''I adm add'''
    time.sleep(5)
    return x+y
print(add(5,6))


1、functools模块 partial方法

  •  偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一个新的函数并返回
  •  从partial生成的新函数,是对原函数的封
1、partial方法举例    :

    import functools
    def add(x,y) -> int:
        return x + y
    newadd = functools.partial(add,y=5)
    print(newadd(7))
    print(newadd(7,y=6))
    print(newadd(y=10,x=6))

    import inspect
    print(inspect.signature(newadd))
2、@functool.lru_cache(maxsize=128,type=False)装饰器
    如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长
    当maxsize是二的幂时,LRU功能执行的最好
    如果typed设置为Ture,则不同类型的函数参数将单独缓存:如:f(3)和f(3.0)将被视为具有
    不同结果的不同调用

3、lru_cache装饰器
    通过一个字典缓存被装饰器的调用和返回值

 

五、参数注解

  •  函数参数的检查,一定是在函数外
  •  函数应该作为参数,传入到检查函数中
  •  检查函数拿到函数传入的实际参数,与形参声明对比
  •  使用inspect模块,提供获取对象信息的函数,可以检查函数和类,类型检查

1、inspect模块

  •  signature(callable),获取签名(函数签名包含了一个函数的信息,包括函数名,它的参数类型,它所在的类和名词空间)
  •  inspect.isfunction(add); 是否是函数
  •  inspect.ismethod(add); 是否是类的方法
  •  inspect.isgenerator(add);是否是生成器对象
  •  inspect.isclass(add);是否是类
  •  inspect.ismodule(inspect);是否是模块
  •  inspect.isbuiltin(print));是否是内建函数
    
>>> import inspect
>>> def add(x:int,y:int,*args,**kwargs) -> int:
        return x + y

>>> sig = inspect.signature(add)
>>> print(sig)
(x:int, y:int, *args, **kwargs) -> int

>>> print('params:',sig.parameters) #有序字典
params: OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])

>>> print('return:',sig.return_annotation)
return: <class 'int'>

>>> print(sig.parameters['y'])
y:int

>>> print(sig.parameters['args'])
*args

>>> print(sig.parameters['args'].annotation)
<class 'inspect._empty'>
>>>     

使用举例
import inspect
def add(x,y:int=7,*args,z,t=10,**kwargs)-> int:
    return x + y
sig = inspect.signature(add)
print(sig)
print('params:',sig.parameters)
print('return:',sig.return_annotation)
print('~~~~~~~~~~')
for i,iterm in enumerate(sig.parameters.items()):
    name,param = iterm
    print('=========',i+1,name,param.annotation,param.kind,param.default)
    print(param.default is param.empty,end='\n\n')

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!