zoukankan      html  css  js  c++  java
  • Python基础(六)—函数式编程(内部函数、闭包、lambda、filter/map/reduce/sorce、偏函数)

    内部函数

    Python中函数的作用域由def关键字界定,函数内的代码访问变量的方式是从其所在层级由内向外,若往外直至全局作用域都查找不到的话代码会抛异常。

    主要看以下代码的差别~~

      """
      def f1():
          x = 5
          def f2():
              x *= x
              return x
          return f2
      """
    
      def f1():
          x = [5]
          def f2():
              x[0] *= x[0]
              return x[0]
          return f2()
    
      print(f1())
    
      def f1():
          x = [5]
          def f2():
              x[0] *= x[0]
              return x[0]
          return f2
    
      print(f1()())
    
      def f1():
          x = 5
          def f2():
              nonlocal x
              x *= x
              return x
          return f2
      print(f1()())
    

    闭包 closure

    • 闭包的一些定义

      • 闭包的定义:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。

      • python中闭包的定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

      • 闭包函数的必要条件:

        • 闭包函数必须返回一个函数对象
        • 闭包函数返回的那个函数必须引用外部变量(一般不能是全局变量),而返回的那个函数内部不一定要return
        • 闭包函数引用的外部变量不一定就是其父函数的参数,也可以是父函数作用域内的任意变量

        下面三个例子为常见的闭包

          # NO.1
          def line_conf(a, b):
              def line(x):
                  return a * x + b
      
              return line
      
      
          # NO.2
          def line_conf():
              a = 1
              b = 2
      
              def line(x):
                  print(a * x + b)
      
              return line
      
      
          # NO.3
          def _line_(a, b):
              def line_c(c):
                  def line(x):
                      return a * (x ** 2) + b * x + c
      
                  return line
      
              return line_c
      
      
    • 为何称作闭包
      按照命令式语言的规则,ExFunc函数只是返回了内嵌函数InsFunc的地址,在执行InsFunc函数时将会由于在其作用域内找不到sum变量而出 错。而在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。
      以下列子:

        def line_conf(a):
          b=1
          def line(x):
              return a * x + b
          return line
      
        line_A = line_conf(2)
        b=20
        print(line_A(1))  # 3
      
      • line_A对象作为line_conf返回的闭包对象,它引用了line_conf下的变量b=1,在print时,全局作用域下定义了新的b变量指向20,最终结果仍然引用的line_conf内的b。这是因为,闭包作为对象被返回时,它的引用变量就已经确定(已经保存在它的__closure__属性中),不会再被修改。
      • 即闭包函数被当作变量返回时,它的所有变量就已经固定,形成了一个封闭的对象,这个对象包含了其引用的所有外部、内部变量和表达式。当然,闭包的参数例外。
    • 显式查看闭包
      __closure__属性返回的是一个元组对象,包含了闭包引用的外部变量。
      根据NO.1,可以使用一些代码查看闭包信息:

        L = line_conf()
        print(line_conf().__closure__) #(<cell at 0x05BE3530: int object at 0x1DA2D1D0>,
        for i in line_conf().__closure__: 
            print(i.cell_contents)  # 打印引用的外部变量值,为1, 2
      
    • 闭包的作用
      闭包最主要的作用是提高代码的可复用性,根据NO.1,输出两条直线:

        #定义两条直线
        line_A = line_conf(2, 1) #y=2x+b
        line_B = line_conf(3, 2) #y=3x+2
      
        #打印x对应y的值
        print(line_A(1)) #3
        print(line_B(1)) #5
      
        # 不使用闭包的代码
        def line_A(x):
            return 2*x+1
        def line_B(x):
            return 3*x+2
      
        print(line_A(1)) #3
        print(line_B(1)) #5
      

      两条直线看不出区别,当需要定义几十几百条,闭包优势就出来了。

    • 闭包的实际运用

      • 例子1:

          def who(name):
              def do(what):
                  print(name, 'say:', what)
        
              return do
        
          lucy = who('lucy')
          john = who('john')
        
          lucy('i want drink!')
          lucy('i want eat !')
          lucy('i want play !')
          
          john('i want play basketball')
          john('i want to sleep with U,do U?')
        
          lucy("you just like a fool, but i got you!")
        

    匿名函数 lambda

    • 特性
      使用lambda可以精简代码、不需要考虑重名函数、可读性,lambda argument_list: expression,特性如下:
      • lambda函数是匿名的:所谓匿名函数,通俗地说就是没有名字的函数。lambda函数没有名字;
      • lambda函数有输入和输出:输入是传入到参数列表argument_list的值,输出是根据表达式expression计算得到的值
      • lambda函数一般功能简单:单行expression决定了lambda函数不可能完成复杂的逻辑,只能完成非常简单的功能。(不支持if else等逻辑判断)
    • 示例:
      • lambda x, y: xy;函数输入是x和y,输出是它们的积xy
      • lambda:None;函数没有输入参数,输出是None
      • lambda *args: sum(args); 输入是任意个数的参数,输出是它们的和(隐性要求是输入参数必须能够进行加法运算)
      • lambda **kwargs: 1;输入是任意键值对参数,输出是1

    映射map/reduce

    • map
      • 功能: 求一个序列或者多个序列进行函数映射之后的值,就该想到map这个函数,它是python自带的函数,在python3.*之后返回的是迭代器,同filter,需要进行列表转换
      • 调用: map(function,iterable1,iterable2),function中的参数值不一定是一个x,也可以是x和y,甚至多个;后面的iterable表示需要参与function运算中的参数值,有几个参数值就传入几个iterable
      • 实例:
        x = [1,2,3,4,5]
        y = [2,3,4,5,6]
        list(map(lambda x,y:(x*y)+2,x,y))
        # 输出:
        [4, 8, 14, 22, 32]
      
    • reduce
      从python 3 开始移到了 functools 模块
      • reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。
      • reduce()还可以接收第3个可选参数,作为计算的初始值
    def f(x, y):
        return x + y
    
    reduce(f, [1, 3, 5, 7, 9])  # 25
    
    reduce(f, [1, 3, 5, 7, 9], 100)  # 125,计算初始值和第一个元素:f(100, 1),结果为101
    

    过滤器 filter

    • 功能: filter的功能是过滤掉序列中不符合函数条件的元素,当序列中要删减的元素可以用某些函数描述时,就应该想起filter函数。

    • 调用: filter(function,sequence),function可以是匿名函数或者自定义函数,它会对后面的sequence序列的每个元素判定是否符合函数条件,返回TRUE或者FALSE,从而只留下TRUE的元素;sequence可以是列表、元组或者字符串

    • 实例:

        x = [1,2,3,4,5]
        list(filter(lambda x:x%2==0,x)) # 找出偶数。python3.*之后filter函数返回的不再是列表而是迭代器,所以需要用list转换。
        # 输出:
        [2, 4]
      

    sorted、sort

    • 基本
      前面也说有高阶函数:map/reduce、filter。sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。
    • 默认是升序排序,可用:reverse=True 实现倒叙排序。
    • 默认情况下,对字符串排序,是按照ASCII的大小比较的,由于’Z’ < ‘a’,结果,大写字母Z会排在小写字母a的前面。可用:key=str.lower 忽略首字母的大小写。
    • sorted和sort
      • sort方法是列表类型list的内置方法,使用sort方法对list排序会修改list本身,不会返回新的list,sort方法只能用于列表,不能对字典、元祖等其他可迭代对象进行排序。
        list.sort( key=None, reverse=False)
      • sorted() 函数能对所有可迭代的对象进行排序操作,sorted()函数不会改变原来的对象,而是会返回一个新的已经排序好的对象。
        sorted(iterable, key=None, reverse=False)
    • 排序与lambda
      lambda可用实现字典key或者是value的排序,又或者是JSON格式的排序。样例如代码:
    list = [2, 5, -4, 11, 7]
    print(list.sort())
    print(list)
    print(sorted(list, reverse=True))
    
    dict = {'a':2,'E':3,'f':8,'d':4}
    print(sorted(dict))
    print(sorted(dict, reverse=True))
    print(sorted(dict, reverse=True, key=str.lower))
    print(sorted(dict.items(), key=lambda x : x[1]))
    
    array = [{"age":20,"name":"a"},{"age":25,"name":"b"},{"age":10,"name":"c"}]
    array = sorted(array,key=lambda x:x["age"], reverse=False)
    print(array)
    

    偏函数

    functools模块提供有wrap与偏函数,可以设定参数的默认值,降低函数的调用难度。

    例如,我们有个需求,需要将二进制转为int,那应该是:

    int(‘100000101’, base=2) # 261

    每次转换都这样,比较麻烦,于是写了函数int2

    def int2(num, base=2):
      return int(num, base)
    

    使用functools.partial就是帮助创建一个这样的偏函数,不用自己定义int2()

    import functools
    
    int2 = functools.partial(int, base=2)
    int2('101010110')
    


    创建偏函数时,实际上可以接受 函数对象、*args、**kw 这三个参数,以上例子实际上固定了int()函数的关键参数base,也就是:
    int2(‘101010110’)
    相当于:
    kw = {‘base’, 2}
    int(‘101010110’, **kw)
    当传入:
    max2 = functools.partial(max, 10)
    实际上会将10作为 *args 的一部分自动加到左边,也就是:
    max2(4, 9) 相当于: max2(10, 4, 9)
    偏函数用于固定住原函数的部分参数,使得调用更简单。

    个人博客:Loak 正 - 关注人工智能及互联网的个人博客
    文章地址:Python基础(六)—函数式编程(内部函数、闭包、lambda、filter/map/reduce/sorce、偏函数)

  • 相关阅读:
    python3 生成器&迭代器
    python3 装饰器
    python3 函数的形参、实参、位置参数、默认参数、关键字参数以及函数的递归
    python3 对文件的查找、替换、删除
    python3 字典相关函数
    python3 字符串相关函数
    spring定时任务-文件上传进度条
    linux系统下开发环境安装与配置
    java中的逃逸分析
    elastic
  • 原文地址:https://www.cnblogs.com/l0zh/p/13739757.html
Copyright © 2011-2022 走看看