Python高阶函数和装饰器

高阶函数

  • First Class Object
    • 函数在Python中是一等公民
    • 函数也是对象,可调用(callable)的对象
    • 函数可以作为普通变量、参数、返回值等等
  • 高阶函数
    • 数学概念y=g(f(x))
    • 在Python中,高阶函数应该满足下列至少一个条件
      1. 接受一个或者多个函数作为参数
      2. 输出一个函数
  • 计数器
    def counter(base):
        def inc(step=1):
            base += step
            return base
        return inc
    
    分析:
        - 函数counter是一个高阶函数,因为它返回了一个函数对象
        - 上述函数的问题在于内层函数使用外层函数的本地变量,但同时有修改它,所以调用是会报错UnboundLocalError,想要更改只需要使用关键字nonlocal在要改变前声明它是外层函数的变量
        - 调用应该使用counter(base)(step)
        - f1 =counter(5)和f1 = counter(5)是不相等的,因为上述调用是两次调用,这样他们的ID就是不一样的,所以不相等

自定义sort函数

  • 排序问题:仿照内建函数sorted,自行实现一个sort函数(不使用内建函数),能够为列表元素排序
  • 思路
    • 内建函数sorted是返回一个新的列表,可以设置升序或降序,可以设置一个排序函数。自定义的函数也应该有类似功能
    • 新建一个列表,遍历原列表,和新列表的值依次比较决定如何插入到新列表中
  • 函数实现1。判断以下代码是如何排序,还能优化么?
    def sort(iterable):
        ret = []
        for x in iterable:
            for i,y in enumerate(ret):
                if x > y:       #找到大的就插入。如果换成x<y,函数就是另一种排序
                    ret.insert(i,x)     #降序
                    break
        else:       #不大于说明是最小的,尾部追加
            ret.append(x)
        return ret 
    
    print(sort([1,7,2,9,3,6,8,4,5]))
  • sort函数实现2。用一个参数控制顺序
    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,7,2,9,3,6,8,4,5]))
  • sort函数实现3。
    def sort iterable,fn=lambda a,b:a>b):
        ret = []
        for x in iterable:
            for i,y in enumerate(ret);
                if fn(i,x):     #函数返回值是bool型
                    ret.insert(i,x)
                    break
            else:
                ret.append(x)
        return ret
    
    print(sort([1,7,2,9,3,6,8,4,5]))

内建函数-高阶函数

  • sorted(iterable[,key][,reverse])排序
    1. 返回一个新列表1,对一个可迭代对象的所有元素排序,排序规则为可以定义的函数,reverse表示是否排序翻转
    2. sorted(lst,key=lambda x:6-x) #返回新列表
    3. list.sort(key=lambda x:6-x) #就地修改
  • filter(function,iterable)
    1. 过滤可迭代对象的元素,返回一个迭代器
    2. function是一个具有一个参数的函数,返回bool
    3. 例如,过滤除被3整除的数字:list(filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123]))
  • map(function,*iterables) –> map object
    1. 对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器
    2. 例子:
      • list(map(lambda x:2*x+1,range(5)))
      • dict(map(lambda x:(X%5,x),range(500)))

柯里化

  • 指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数
  • z = f(x,y)转换成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)
    
    通过嵌套函数就可以吧函数转换称柯里化函数

装饰器

  • 需求
    • 一个加法函数,想要加强它的功能,能够输出被调用过以及调用的参数信息
      def add(x,y):
          return x+y
      
      增加信息输出功能
      def add(x,y):
          print('call add,{}+{}'.format(x,y))
          return x+y
    • 上面的加法函数是完成了需求,但是有以下缺点:
      1. 打印语句的耦合太高
      2. 加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不应该放在业务函数内部
  • 推演过程
    首先将业务函数和增强功能分开,但发现fn函数调用传参是个问题
    def add(x,y):
        return x + y
    
    def logger(fn):
        print('begin')
        x = fn(4,5)
        print('end')
        return x
    
    print(logger(add))
    
    解决传参的问题
    def add(x,y):
        return x + y
    
    def logger(fn,*args,**kwargs):    #这里*args和**kwargs代表可变参数
        print('begin')
        x = fn(*args,**kwargs)      #*args,**kwargs在这表示参数解构
        print('end')
        return x
    
    print(logger(add,5,y=50))
    
    柯里化
    def add(x,y):
        return x + y
    
    def logger(fn):
        def _logger(*args,**kwargs):
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        return _logger
    
    print(logger(add)(5,y=50))  
    #最后也可以这么写
    # add = logger(add)
    # print(add(x=5,y=10))
  • 装饰器语法
def logger(fn):
    def _logger(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return _logger
    
@logger #等价于add= logger(add),这就是装饰器语法
def add(x,y):
    return x+y
    
print(45,50)
  • 上面的语法是无参装饰器
    1. 它是一个函数
    2. 函数作为他的形参
    3. 返回值也是一个函数
    4. 可以使用@functionname方式,简化调用
  • 装饰器是高阶函数,但装饰器对传入函数的功能的装饰(功能增强)

文档字符串

  • Python中文档字符串Documentation Strings
  • 在函数体语句块的第一行,且习惯是多行的文本,所以多使用三引号
  • 惯例是首字母大写,第遗憾写概述,空遗憾,第三行写详细描述
  • 可以是使用特殊属性__doc__访问这个文档
def add(x,y):
    '''This is a function of addition'''
    a = x+y
    return a

print('name={}\ndoc={}'.format(add.__name__,add.__doc__))
print(help(add))

使用装饰器的副作用

  • 使用装饰器后原函数对象的属性都被替换了,而使用装饰器,希望查看原函数的属性,如何解决
    1. 可以自己定义一个函数将被包装函数的属性覆盖掉包装函数
    def copy_properties(src,dst):   #自定义复制属性的函数
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        
    
    def logger(fn):
        def _logger(*args,**kwargs):
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        copy_properties(fn,_logger) #函数调用,复制被包装函数属性覆盖包装函数属性
        return _logger
    
    @logger 
    def add(x,y):
        return x+y
    
    print(45,50)
    1. 凡是被装饰的函数都需要赋值这些属性,这个函数很通用
    2. 可以将被复制属性的函数构建成装饰器函数,带参装饰器
    def copy_properties(src):   #柯里化
        def _copy(dst):
            dst.__name__ = src.__name__
            dst.__doc__ = src.__doc__
            return dst
        return _copy
    
    def logger(fn):
        @copy_properties(fn)    #_logger = copy_properties(fn)(_logger)
        def _logger(*args,**kwargs):
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        
        return _logger
    
    @logger 
    def add(x,y):
        """this is """        
        return x+y
    
    print(45,50)

带参装饰器

  • 它是一个函数
  • 函数作为他的形参
  • 返回值是一个不带参的装饰器函数
  • 使用@functionname(参数列表)方式调用
  • 可以看作在装饰器外层又加了一层函数
def logger(duration):
    def _logger(fn):
        def wrapper(*args,**kwargs):
            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(5)
def add(x,y):
    time.sleep(3)
    return x+y
print(add(5,6))
  • 将记录的功能提取出来,就可以通过外部提供的哈数来灵活的控制输出
def logger(duration,func=lambda name,duration:print('{}took{}s'.format(name,duration))):
    def _logger(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)
            print('so slow') if delta >duration else print('so fast')
            return ret
        return wrapper
    return _logger

functools模块

  • functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
    • 类似copy_properties功能
    • wrapper包装函数,wrapped被包装函数
    • 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性
    • 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
    • 增加一个__wrapped__属性,保留着wrapped函数
  • functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)和上个方法用法基本相同

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88009

赞 (0)
KX_ilKX_il
上一篇 2017-10-22 23:25
下一篇 2017-10-23 09:08

相关推荐

  • 作业-第二周

    1、linux常用文件管理命令 ls rm chmod touch mv cp 2、echo $? 如果返回0则成功,其他失败 3、 mkdir /tmp/{a,b}_{c,d} mkdir -p /tmp/mylinux/bin /tmp/mylinux/boot/ /tmp/mylinux/boot/grup /tmp/mylinux/dev \ /tm…

    Linux干货 2016-06-23
  • FHS文件系统各目录功能

    前言:     文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易。linux的哲学是一切皆文件,linux系统中的一切包括设备文件,我们都能在linux的倒置树状文件系统中找到相对应的文件。所以清楚的明白根目录下的每个子目录包含什么样的文件是很有必要的。我们将以Filesystem Hierarchy Standar…

    Linux干货 2016-10-17
  • varnish

    Web Page Cache: squid –> varnish程序的运行具有局部性特征:时间局部性:一个数据被访问过之后,可能很快会被再次访问空间局部性:一个数据被访问时,其周边的数据也有可能被访问到 cache:命中 热区:局部性;时效性:缓存空间耗尽:LRU过期:缓存清理 缓存命中率:hit/(hit+miss)(0,1)页面命中率:…

    Linux干货 2017-05-22
  • HA Cluster 之 vrrp (keepalived)

      HA Cluster 之 vrrp (keepalived) 0x00 前言 0x01 VRRP 协议 VRRP 简介 VRRP 工作原理 VRRP负载分担方式的特点 VRRP 中的一些术语 0x02 keepalived 功用: 0x03 keepalived 配置 HA Cluster 的配置前提: 配置文件组件部分: 配置文件解析: 附:…

    Linux干货 2017-05-16
  • LNMMP架构实现Web动静分离

    前言 前面的文章中说过LAMP架构包括:Linux操作系统,Apache网站服务器,MySQL数据库,Perl、PHP或者Python编程语言,而今天要说的LNMMP 和LAMP类似,只是作为Web服务器的不再是Apache而是高性能的Nginx,同时引进Memcached加速缓存效率,用于加快访问速度。 Memcached是一款开源、高性能、分布…

    Linux干货 2015-06-15