14高阶函数_柯里化_装饰器_functools_docstring

 

专业成都网站建设公司,做排名好的好网站,排在同行前面,为您带来客户和效益!创新互联公司为您提供成都网站建设,五站合一网站设计制作,服务好的网站设计公司,成都网站设计、成都网站制作负责任的成都网站制作公司!

高阶函数、柯里化、装饰器、functools、文档字符串

 

 

目录

高阶函数...1

内建函数-高阶函数...3

currying柯里化:...4

decorator装饰器:...4

装饰器(无参):...7

带参装饰器:...9

functools:...12

文档字符串:...14

 

 

 

高阶函数

函数:

first class object,函数在python中是一等公民;

函数也是对象,可调用的对象;

函数可以作为普通变量、参数、返回值等;

 

高阶函数

数学概念,y=g(f(x));

在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数:

接受一个或多个函数作为参数;

输出一个函数,如return inc;

 

计数器:

例:

In [1]: def counter(base):   #函数counter()是一个高阶函数

   ...:     def inc(step=1):

   ...:         nonlocal base

   ...:         base += step   #赋值前引用,用nonlocal解决

   ...:         return base

   ...:     return inc

   ...:

In [2]: foo=counter(10)

In [3]: foo1=counter(10)

In [4]: foo==foo1  #foo和foo1不一样,可理解为inc为counter内的标识符,每次counter()运行是不同的对象

Out[4]: False

In [5]: id(foo)

Out[5]: 139964301851096

In [6]: id(foo1)

Out[6]: 139964302182600

In [7]: foo()

Out[7]: 11

In [8]: foo()

Out[8]: 12

In [9]: foo1()

Out[9]: 11

In [10]: foo1()

Out[10]: 12

 

自定义一个sort函数:

排序问题,参照内建函数sorted,自行实现一个sort函数(不使用内建函数),能够为列表元素排序;

需求:原列表不变(sorted(lst));原列表改变(lst.sort());

思路:

内建函数sorted返回一个新列表,可设置升序降序,可设置一个排序的函数,自定义sort函数也要具备这些功能;

新建一个列表,遍历原列表,和新列表的值依次比较决定如何插入到新列表中;

sorted函数的实现原理,扩展到map、filter函数;

 

例:

def sort(iterable):

    ret = []

    for x in iterable:

        for i,y in enumerate(ret):

            if x > y:

                ret.insert(i,x)

                break

        else:

            ret.append(x)

    return ret

 

sort([5,3,2,1,4])

#######################

def sort(iterable,reverse=False):

    ret = []

    for x in iterable:

        for i,y in enumerate(ret):

            flag = x < y if reverse else flag = x > y

            if flag:

                ret.insert(i,x)   #找到大的就插入,降序

                break

        else:

            ret.append(x)   #小的尾部追加

    return ret

 

sort([5,3,2,1,4],reverse=True)

#######################

def sort(iterable,key=lambda a,b:a

    ret = []

    for x in iterable:

        for i,y in enumerate(ret):

            if key(x,y):

                ret.insert(i,x)

                break

        else:

            ret.append(x)

    return ret

 

sort([5,3,2,1,4],lambda a,b:a>b)

#########################

 

 

 

内建函数-高阶函数

sorted(iterable[,key][,reverse])-->new list,排序,返回一个新列表,对一个可迭代对象的所有元素排序,排序规则为key定义的函数,可用reverse指定排序翻转;

例:

In [11]: lst = [3,2,4,5,1]

In [12]: sorted(lst,key=lambda x:6-x)   #内置sorted的key可对每个元素进行变化,该变化只在比较时用于临时值,比较后的新列表中的元素仍然是原列表中的元素

Out[12]: [5, 4, 3, 2, 1]

In [13]: lst = ['a',1,2,3,'b']

In [15]: sorted(lst,key=str)   #key=str为比较逻辑

Out[15]: [1, 2, 3, 'a', 'b']

 

filter(function,iterable)-->filter object,generator,过滤数据,过滤可迭代的元素,返回一个迭代器;function是一个具有一个参数的函数,返回bool;

例:

In [16]: filter(lambda x:x%3==0,[1,9,55,150,-3.78,28,123])

Out[16]:

In [17]: list(filter(lambda x:x%3==0,[1,9,55,150,-3.78,28,123]))   #过滤出列表中能被3整除的数字;注意function中要有参数x,否则报错

Out[17]: [9, 150, 123]

 

map(function,*iterables)-->map object,generator,映射,对多个可迭代对象的元素按指定的函数进行映射,返回一个迭代器;

例:

In [18]: map(lambda x:2*x+1,range(5))

Out[18]:

In [20]: list(map(lambda x:2*x+1,range(5)))   #可用此方式获取奇数、偶数

Out[20]: [1, 3, 5, 7, 9]

In [21]: dict(map(lambda x:(x%5,x),range(500)))   #构造字典,通过key已去重(后面覆盖前面的了)

Out[21]: {0: 495, 1: 496, 2: 497, 3: 498, 4: 499}

 

 

 

currying柯里化:

指将原来接受2个参数的函数变成新的接受一个参数的过程,新的函数返回一个以原有第二个参数为参数的函数;

z=f(x,y)-->z=f(x)(y),这种形式;

例:

将加法函数柯里化:

In [22]: def add(x,y):

    ...:     return x+y

    ...:

In [23]: def add(x):

    ...:     def _add(y):

    ...:         return x+y

    ...:     return _add

    ...:

 

 

 

decorator装饰器:

装饰器的用途:

装饰器是AOP思想的体现,aspect oriented programming面向切面编程;

面向对象往往需要通过继承或组合依赖等方式调用一些功能,这些功能的代码往往可能在多个类中出现,如logger,这样造成代码的重复,增加了耦合,logger的改变影响所有使用它的类或方法;

而AOP在需要的类或方法上切下,前后的切入点可加入增强的功能,让调用者和被调用者解耦;

这是一种不修改原来的业务代码,给程序动态添加功能的技术,如logger函数功能就是对业务函数增加日志的,而业务函数中应把与业务无关的日志功能剥离干净;

 

装饰器应用场景:

日志、监控、权限、设计、参数检查、路由等;

这些功能与业务功能无关,很多业务都需要公共功能,所以适合独立出来,需要的时候对目标对象增强;

 

需求:

加法函数,增强其功能,输出被调用过及调用的参数信息;

例:

def add(x,y):

    print('call: {},{}+{}'.format(add.__name__,x,y))

    return x+y

 

add(1,2)

注:

此方式完成了需求,但有缺点;

打印语句的耦合太高,与定义的函数紧紧关联;

加法函数属于业务功能,而输出信息是非业务功能代码,不该放在业务函数中,这称为侵入式代码;

##################

def add(x,y):

    return x+y

 

def logger(fn):

    print('begin')

    x=fn(4,5)

    print('end')

    return x

 

print(logger(add))

注:

虽做到了业务功能分离,但fn函数调用传参是个问题;

##################

del add

 

def add(x,y,*args):

    return x+y

 

def logger(fn,x,y):

    print('begin')

    ret=fn(x,y)

    print('end')

    return ret

 

print(logger(add,4,5))

###################

def add(x,y,z):

    return x+y+z

 

def logger(fn,*args,**kwargs):   #可变参数,解决了传参问题

    print('begin')

    ret=fn(*args,**kwargs)   #参数解构

    print('end')

    return ret

 

print(logger(add,4,z=5,y=6))

###################

def add(x,y,z):

    return x+y+z

 

def logger(fn):   #在上例基础上将logger柯里化

    def _logger(*args,**kwargs):   #可变参数

        print('begin')

        ret=fn(*args,**kwargs)   #参数解构;闭包(fn为自由变量)

        print('end')

        return ret

    return _logger   #返回内部函数的引用

 

print(logger(add)(4,5,6))

 

foo=logger(add)

print(foo(4,5,6))   #等价于print(logger(add)(4,5,6))

 

add=logger(add)   #等价于@logger

print(add(4,5,6))

#####################

def logger(fn):   #如果此处用两个参数def logger(fn,x),后面使用@logger时会语法错误,解决办法,进一步柯里化

    def _logger(*args,**kwargs):   #可变参数

        print('begin')

        ret=fn(*args,**kwargs)   #参数解构;此处闭包,正因为闭包原函数在@logger(即add=logger(add))被保留下来;此处如果写为return ret=fn(*args,**kwargs)后面语句将不会执行

        print('end')

        return ret   #若无此行会破坏原函数

    return _logger   #外层函数应返回内层函数的引用

 

@logger   #等价于add=logger(add);第一个add已指向内层函数_logger一般起名为wrapper包装函数;第二个add(即logger(add))为原函数;@logger要放到被包装函数的上一行

def add(x,y):

    return x+y

 

print(add(4,5))   #此处add不是原函数,而是被包装后的内层函数(即add=logger(add))

注:

外层函数应返回内层函数的引用;

外层函数的参数应为要包装的函数,add=logger(add);

@logger即装饰器的语法;

 

 

 

装饰器(无参):

它是一个函数;

函数作为它的形参;

返回值也是一个函数;

可以使用@function方式,简化调用;

 

装饰器和高阶函数:

装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强);

 

例:

import datetime

import time

 

def logger(fn):

    def wrapper(*args,**kwargs):

        print('args={},kwargs={}'.format(args,kwargs))

        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 wrapper

 

@logger

def add(x,y):

    print('=========call add===========')

    time.sleep(2)

    return x+y

 

print(add(4,y=5))

 

例:

def copy_properties(src,dst):

    dst.__name__ = src.__name__

    dst.__doc__ = src.__doc__

    dst.qualname__ = src.__qualname__

   

def logger(fn):

    def wrapper(*args,**kwargs):

        '''This is a wrapper'''

        print('begin')

        ret = fn(*args,**kwargs)

        print('end')

        return ret

    copy_properties(fn,wrapper)

    return wrapper

 

@logger

def add(x,y):

    '''

    This is a additional.

   

    return int

    x int

    y int

    '''

    ret = x + y

    return ret

 

print(add.__name__,add.__doc__,add.__qualname__,sep='\n')

#######################

def copy_properties(src):

    def wrapper(dst):

        dst.__name__ = src.__name__

        dst.__doc__ = src.__doc__

        dst.qualname__ = src.__qualname__

        return dst

    return wrapper

   

def logger(fn):

    @copy_properties(fn)   #等价于wrapper=copy_properties(fn)(wrapper),出现的wrapper可认为都是logger内的wrapper,copy_properties(fn)即copy_properties内的wrapper

    def wrapper(*args,**kwargs):

        '''This is a wrapper'''

        print('begin')

        ret = fn(*args,**kwargs)

        print('end')

        return ret

    #copy_properties(fn,wrapper)

    return wrapper

 

@logger

def add(x,y):

    '''

    This is a additional.

   

    return int

    x int

    y int

    '''

    ret = x + y

    return ret

 

print(add.__name__,add.__doc__,add.__qualname__,sep='\n')

注:

包装函数属性改为被包装函数属性;

调用时用到的是嵌套函数内层wrapper的属性而不是原函数add的属性,在使用copy_properties函数后,可让使用者认为是原函数的属性;

通过copy_properties函数将被包装函数的属性覆盖掉包装函数;

凡是被装饰的函数都要复制这些属性,上例很通用;

可以将复制属性的函数构建成装饰器函数,带参装饰器;

 

 

 

带参装饰器:

是一个函数;

函数作为它的形参;

返回值是一个不带参的装饰器函数(不一定);

使用@function(参数列表)方式调用;

可以看作在装饰器外层又加了一层函数;

最多也就三层;

需求:

获取函数的执行时长,对长超过阈值的函数记录一下;

例:

import datetime

import time

 

def logger(t):

    def _logger(fn):

        def wrapper(*args,**kwargs):

            '''This is a wrapper.'''

            print('args={},kwargs={}'.format(args,kwargs))

            start = datetime.datetime.now()

            ret = fn(*args,**kwargs)

            duration = (datetime.datetime.now() - start).total_seconds()

            if duration > t:

                print('function {} took {}s'.format(fn.__name__,duration))

            return ret

        return wrapper

    return _logger

 

@logger(3)   #等价于add=logger(3)(add)

def add(x,y):

    print('===============call add================')

    time.sleep(4)

    return x+y

 

print(add(4,5))

###########################

import datetime

import time

 

def copy_properties(src):

    def wrapper(dst):

        dst.__name__ = src.__name__

        dst.__doc__ = src.__doc__

        dst.__qualname__ = src.__qualname__

        return dst

    return wrapper

 

def logger(duration):

    def _logger(fn):

        @copy_properties(fn)  #等价于add=copy_properties(add)(dst)

        def wrapper(*args,**kwargs):

            '''This is a wrapper.'''

            start = datetime.datetime.now()

            ret = fn(*args,**kwargs)

            delta = (datetime.datetime.now() - start).total_seconds()

            print('so slow') if delta > duration else print('so fast')

            return ret

        return wrapper

    return _logger

 

@logger(3)  #等价于add=logger(3)(add)

def add(x,y):

    print('===============call add================')

    time.sleep(2)

    return x+y

 

print(add(4,5))

##########################

def logger(fn):   #无论logger(add)是什么都返回10,语法允许,在装饰器角度并没有装饰什么,把原函数add给废掉了

    return 10

 

@logger   #add=logger(add)

def add(x,y):

    return x+y

 

print(add)

############################

import datetime

import time

 

def copy_properties(src):

    def wrapper(dst):

        dst.__name__ = src.__name__

        dst.__doc__ = src.__doc__

        dst.__qualname__ = src.__qualname__

        return dst

    return wrapper

 

def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):

    def _logger(fn):

        @copy_properties(fn)

        def wrapper(*args,**kwargs):

            '''This is a 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(3)

def add(x,y):

    print('===============call add================')

    time.sleep(4)

    return x+y

 

print(add(4,5))

注:

将记录的功能提取出来,这样就可通过外部提供的函数来灵活的控制输出;

 

 

 

functools:

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

类似copy_properties功能;

wrapper包装函数,wrapped被包装函数;

元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性,__module__模块名,__name__名称,__qualname__限定名,__doc__文档,__annotations__参数注解;

元组WRAPEER_UPDATES中是要被更新的属性,__dict__属性字典,注意字典要用update方式;

增加一个__wrapped__属性,保留着wrapped函数;

 

例:

import datetime,time,functools

 

def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):

    def _logger(fn):

        def wrapper(*args,**kwargs):

            '''This is a 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

        functools.update_wrapper(wrapper,fn)

        return wrapper

    return _logger

 

@logger(3)

def add(x,y):

    print('===============call add================')

    time.sleep(4)

    return x+y

 

print(add(4,5),add.__name__,add.__wrapped__,add.__dict__,sep='\n')

#######################

import datetime

import time

import functools

 

def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):

    def _logger(fn):

        @functools.wraps(fn)   #经常用

        def wrapper(*args,**kwargs):

            '''This is a 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(3)

def add(x,y):

    print('===============call add================')

    time.sleep(4)

    return x+y

 

print(add(4,5),add.__name__,add.__wrapped__,add.__dict__,sep='\n')

#######################

import functools

   

def logger(fn):

    @functools.wraps(fn)

    def wrapper(*args,**kwargs):

        '''This is a wrapper'''

        print('begin')

        ret = fn(*args,**kwargs)

        print('end')

        return ret

    print('{} {}'.format(id(wrapper),id(fn)))

    return wrapper

 

@logger

def add(x,y):

    '''

    This is a additional.

   

    return int

    x int

    y int

    '''

    ret = x + y

    return ret

 

print(add.__name__,add.__doc__,add.__qualname__,sep='\n')

print('*'*50)

print(id(add.__wrapped__))

print(add(4,5))

print(add.__wrapped__(4,5))   #用包装函数调用,如add(4,5);而不要用被包装函数调用,如add.__wrapped__(4,5)

 

 

 

文档字符串:

python的文档:

在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号,约定使用双引号;

惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;

可使用特殊属性__doc__访问这个文档;

 

例:

In [28]: def add(x,y):

    ...:     '''

    ...:     This is a funtion of addition.

    ...:    

    ...:     return int

    ...:     x int

    ...:     y int

    ...:     '''

    ...:     return x+y

    ...:

In [29]: print('name={}\ndoc={}'.format(add.__name__,add.__doc__))

name=add

doc=

    This is a funtion of addition.

   

    return int

    x int

    y int

   

 

In [30]: help(add)

……

 

 


本文名称:14高阶函数_柯里化_装饰器_functools_docstring
URL分享:http://azwzsj.com/article/jcshoc.html