• Python学习笔记(六)之函数式编程


    0. Python函数式编程思维导图

    0.1 导图链接

    Python函数式编程

    0.2 导图截图

    • 可能不够清晰看链接就好啦

    TIM截图20200120001944.png

    1.函数式编程(Functional Programming)

    • 在编程语言中(抽象程度是相对于计算机来说的):
      • 越低级的语言,越贴近计算机,抽象程度越低,执行效率越高,如C语言;
      • 越高级的语言,越贴近数学计算,抽象程度高,执行效率越低,如Lisp。
    • 函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量。
    • 函数式编程特点
      • 允许将一个函数作为参数传入到另一个函数中。
      • 允许返回一个函数。
    • Python对函数式编程提供部分支持,由于Python允许使用变量,所以它不是存函数式编程。

    2. 高阶函数

    2.1 高阶函数是什么?

    • 首先了解两个概念

      1. 变量可以指向函数

        • 例2.1.1:

          >>> abs
          <built-in function abs>
          >>> a = abs
          >>> a(-1)
          1
          
        • 分析:函数本身可以赋值给变量。上述例子中被赋值的变量a可以当做abs函数本身来使用。

      2. 函数名也是变量

        • 例2.1.2:

          >>> abs = 10
          >>> abs(-1)
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          TypeError: 'int' object is not callable
          
        • 分析:这里的函数名abs指向了数字10后,我们再以函数的形式调用它,就已经不起作用了,因为abs已经不指向求绝对值的函数,而是指向了10这个整数!说明函数名本身也是变量

    • 搞懂了上面两个概念后,我们再回过头来看高阶函数。

      • 一个函数可以接受另一个函数作为参数,这种函数就是高阶函数

      • 例2.1.3:

        #!/user/bin/python
        #coding=utf-8
        
        _author_ = "zjw"
        
        #一个计算两个列表总长度的高阶函数函数
        def allLen(x, y, fun):
            return fun(x) + fun(y)
        
        if __name__ == "__main__":
            li1 = [1, 2, 3]
            li2 = [4, 5]
            print allLen(li1, li2, len)
        
      • 输出

        5

      • 分析:上述高阶函数allLen是一个计算两个列表长度总和的函数。该函数将len函数作为参数传入到allLen函数中,所以是一个高阶函数。

    2.2 map

    • 基本形式:map(function, Iterable);

      • function指某个特定的函数。
      • Iterable,即凡是可以使用for循环的对象都是Iterable类型的。如list, tuple, dict, set, str等。
    • map函数返回一个Iterator(即凡是可以使用next()函数的对象都是Iterator类型的)。

      • 注:因为Iterator是一个惰性序列,所以在返回值时可以使用list()函数把整个序列编程一个list型。
    • 函数说明:map将传入的函数依次作用到序列的每一个元素,并把结果作为新的Iterator返回。

    • 实例

      • 例2.2.1:

        #!/user/bin/python
        #coding=utf-8
        
        _author_ = "zjw"
        
        if __name__ == "__main__":
            li = [-1, -3, 2, -3]
            newList = list(map(abs, li))
            print newList
        
      • 输出:

        [1, 3, 2, 3]

      • 分析:上面例子中,map函数的作用是将abs函数作用于li列表的每一个元素,返回一个全部都是绝对值的新列表newList;这里在map函数外还嵌套了一个list函数的作用是将惰性序列转化为列表。

    2.3 reduce

    • 基本形式:reduce(function, Iterable);

      • function指某个特定函数。
      • Iterable的说明如上。
    • reduce函数返回的是一个值。

    • 函数说明:。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,效果如下:

      reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
      
    • 实例

      • 例2.3.1:

        #!/user/bin/python
        #coding=utf-8
        
        _author_ = "zjw"
        
        def add(x, y):
            return x + y
        
        if __name__ == "__main__":
            li = range(11)
            sum = reduce(add, li)
            print sum
        
      • 输出

        55

      • 分析:这里的reduce函数是将add函数作为功能函数,一开始传入两个li的元素,得到结果与li的下一个元素再传入add函数中,以此类推,得到最后的总和的结果。

    2.4 filter

    • 基本形式:filter(function, Iterable);

    • 函数返回一个Iterator

    • 函数说明:filter()把传入的函数依次作用到每一个元素上,然后根据函数的返回值是True还是False决定保留还是丢弃该元素。即就像一个过滤器一样,过滤掉我们不想要的元素。

    • 实例:

      • 例2.4.1:

        #!/user/bin/python
        #coding=utf-8
        
        _author_ = "zjw"
        
        #判断是否为偶数,偶数返回True,反之返回False
        def isEven(x):
            return x % 2 == 0
        
        if __name__ == "__main__":
            li = [1, 2, 3, 4]
            newList = list(filter(isEven, li))
            print(newList)
        
      • 输出:

        [2, 4]

      • 分析:这里的isEven函数是一个功能函数,用来判断是否为偶数,再通过filter函数进行筛选,将li列表中不是偶数的元素筛选掉,左右输出的是只有偶数的序列。

    2.5 sorted

    • sorted (iterable, *, key=None, reverse=False)

      • iterable 即一个可以使用for循环的序列。
      • *是一个特殊分隔符,后面的两个参数时命名关键字参数。
        • key中可以传入一个函数,用来指定排序规则,即映射函数,是排序的灵魂所在。
        • reverse则是决定最后输出序列是递增还是递减序列,默认是递增,即reverse = False。想要改成递减即reverse = True即可。
    • 函数返回一个递增或递减序列。

    • 函数说明:排序。

    • 实例:

      • 例2.5.1:

        >>> li = ['Z', 'a', 'z', 'c', 'A']
        >>> sorted(li)
        ['A', 'Z', 'a', 'c', 'z']
        
        • 分析:默认情况下对字符串的排序是按照ASCII的大小进行排序的,其中大写字母'Z'<'a',所以大写Z排在小写a之前。且默认情况下排序结果是递增序列。
      • 改进2.0,例2.5.2:

        >>> li = ['Z', 'a', 'z', 'c', 'A']
        >>> sorted(li)
        ['A', 'Z', 'a', 'c', 'z']
        >>> sorted(li, key = str.lower)
        ['a', 'A', 'c', 'Z', 'z']
        
        • 分析:通过key关键字参数规定了排序规则为,将所有字母都变成小写字母后再进行排序,且最后得到的排序序列依旧是原来的字母。
      • 改进3.0,例2.5.3:

        >>> li = ['Z', 'a', 'z', 'c', 'A']
        >>> sorted(li)
        ['A', 'Z', 'a', 'c', 'z']
        >>> sorted(li, key = str.lower)
        ['a', 'A', 'c', 'Z', 'z']
        >>> sorted(li, key = str.lower, reverse = True)
        ['Z', 'z', 'c', 'a', 'A']
        
        • 分析:这次将reverse改成True,则函数根据key规则调整后,又将序列通过逆序输出了出来。

    3. 返回函数

    3.1 返回函数是什么?

    • 顾名思义:函数作为返回值的函数。相当于函数中嵌套一个函数,然后实现该函数后,并返回该函数。

    • 将函数作为结果返回也是高阶函数的另一个特性。

    3.2 返回函数实例分析

    • 例3.2.1:

      #!/user/bin/python
      #coding=utf-8
      
      _author_ = "zjw"
      
      
      def lazy_sum(*args):
          def sum():
              ax = 0
              for n in args:
                  ax = ax + n
              return ax
          return sum
      
      if __name__ == "__main__":
          f1 = lazy_sum(1, 2, 3, 4)
          f2 = lazy_sum(5, 6, 7, 8)
          print f1, f2
          print f1(), f2()
      
    • 输出:

      <function sum at 0x0000000002F785F8> <function sum at 0x0000000002F78668>
      10 26

    • 分析:

      • 可以看到,当我们调用f1和f2的时候,得到的是一个函数本身,即lazy_sum内部函数sum。
      • 当我们想要得到内部函数sum所求的值时,我们需要调用f1()f2()来得到。
      • 值得注意的是,这里在函数lazy_sum中又定义了函数sum,且内部函数sum可以引用外部函数 lazy_sum的参数和局部变量。当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”。

    4. 匿名函数(lambda)

    4.1 匿名函数是什么?

    • 所谓匿名,匿名,就是将名字隐藏,或则直接说它是没有名字的函数也不为过。哈哈,Python的匿名函数可以说所有的匿名函数的名字统一都是lambda
    • 匿名函数的存在,将已经非常简约的函数又一次变得更简约,且在某些函数使用次数不多的情况下,可以随心所欲的创建和使用。
    • 相对于java来说,Python只对匿名函数提供了有限的支持。因为Python中的匿名函数只能够有一个表达式。

    4.2 匿名函数实例分析

    • 例4.2.1:

      >>> list(map(lambda x : 2 * x, [1, 2, 3, 4, 5]))
      [2, 4, 6, 8, 10]
      
      • 分析:其中map函数和list的转化已经比较熟悉了。那么来看一下这里的lambda函数lambda x : 2 * x,它就相当于一下的函数:

        def f(x):
            return 2 * x
        
      • 其中lambda函数中的关键字lambda就是用来昭示匿名函数,冒号前面的x表示要传入函数的参数,冒号后的2 * x表示的就是return的内容。

    • 例4.2.2

      >>> f = lambda x, y : x + y
      >>> f(1, 2)
      3
      >>> f('a', 'b')
      'ab'
      
      • 分析:如该lambda函数表达式所示,函数传入两个数据,中间用,号隔开,并返回两个数据的和,当然和也可以是字符串的和。且在该例子中,我们将该匿名函数作为一个对象赋值给了变量f,调用变量f即可调用我们创建的匿名函数。该函数的原型应该如下:

        def f(x, y):
            return x + y
        

    4.3 匿名函数注意点

    • 好处:
      • 因为匿名函数没有名字,所以不必担心函数名冲突的问题。
      • 匿名函数也是一个函数对象,可以赋值给一个变量,再利用变量来调用函数。
    • 需要注意的是,匿名函数有一个限制,就是只能有一个表达式,该表达式的计算结果就是该匿名函数的返回结果。所以只有一些简单的情况下可以使用匿名函数。

    5. 装饰器(Decorator)

    5.1 装饰器是什么?

    • 首先来写一个简单的函数:

      def now():
          print("这里是now函数,时间是2020年1月19日21:57:19。")
      
    • 现在我们有了一个简单的now()函数,当我们想在这个函数的前后加一些装饰,即给他加一些说明时,我们往往会在now函数里面动手脚,但是当我们想要在不动now函数本体的情况下进行一些修改时,我们就要用到装饰器了。

    • 这种在代码运行期间动态增加功能的方式,称之为装饰器

    5.2 装饰器实例分析

    • 实例1:两层嵌套decorator用法

      • 例5.2.1:

        #!/user/bin/python
        #coding=utf-8
        
        _author_ = "zjw"
        
        #用作now()函数的装饰函数
        def log(func):
            def wrapper(*args, **kw):
                print('call %s():' % func.__name__)
                return func(*args, **kw)
            print("现在执行的是wrapper函数")
            return wrapper
        
        @log
        def now():
            print("2020 1 19")
        
        if __name__ == "__main__":
            now()
        
      • 输出:

        现在执行的是wrapper函数
        call now():
        2020 1 19

      • 分析:在调用now函数的时候,实际上我们先调用了@log中的函数内容。Python就是通过@语法,把decorator置于函数的定义处的,并在调用该函数的时候,实际上是执行了now = log(now)
        之所以我们看到先执行了wrapper函数,是因为我们进入到log函数后,先跳过了wrapper函数,执行了print("现在执行的是wrapper函数"),在返回函数wrapper的时候调用了wrapper函数,并输出了下面的内容。我们看到wrapper函数的参数是(*args, **kw),所以该函数可以接受任意参数的调用。

    • 实例2:三层嵌套decorator用法

      • 实例5.2.2:

        #!/user/bin/python
        #coding=utf-8
        
        _author_ = "zjw"
        
        #用作now()函数的装饰函数
        def log(text):
            def decorator(func):
                def wrapper(*args, **kw):
                    print('%s %s():' % (text, func.__name__))
                    return func(*args, **kw)
                print('我现在执行的是decorator函数')
                return wrapper
            print("我现在执行的还是log函数")
            return decorator
        
        @log('execute')
        def now():
            print("2020 1 19")
        
        if __name__ == "__main__":
            now()
        
      • 输出:

        我现在执行的还是log函数
        我现在执行的是decorator函数
        execute now():
        2020 1 19

      • 分析:3层decorator调用实际上效果是:now = log('execute')(now)。首先我们看到执行的是log函数,因为log函数要传参进去,我们传了一个'execute'信息进去,首先我们跳过里面的两层嵌套循环来到了print("我现在执行的还是log函数")语句,在要返回decorator函数时我们进入到decorator函数,其中的func参数就是now函数了,这时我们又先跳过了wrapper函数执行了print('我现在执行的是decorator函数')语句,在返回wrapper函数的时候进入到wrapper函数,执行了wrapper函数的print('%s %s():' % (text, func.__name__))语句,并进入func函数,即执行了now函数中的内容,最后层层返回,先返回到decorator函数,再最后退出了log装饰器。

    6. 偏函数(Partial function)

    6.1 老规矩,偏函数是什么?

    • 就是当函数的参数太多的时候,或则我们需要在某些时候经常改变某些参数的时候,我们需要简化,这时候可以使用(functools.partial)创建一个新的函数,这个函数可以固定住原来的参数的部分参数,从而使我们调用的时候更加的简单。

    6.2 话不多说,直接上实例

    • 例6.2.1:这里以int()函数为例,当我们需要转换大量的二进制字符串时,每次传入int(x, base = 2)非常麻烦,这时可以用下面方法解决。

      #!/user/bin/python
      #coding=utf-8
      
      _author_ = "zjw"
      
      import functools
      
      if __name__ == "__main__":
          int2 = functools.partial(int, base = 2)
          print(int2('101'))
      
    • 输出:

      5

    • 分析:其中int2 = functools.partial(int, base = 2)语句其实可以用下面的函数代替:

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

      两个的实现方式不同,但是能用一行代码解决的我们坚决不用两行。但是呢这只是暂时固定住函数的base参数,如果我们更改base值,调用时也是会发生变化的,如下:

      #!/user/bin/python
      #coding=utf-8
      
      _author_ = "zjw"
      
      import functools
      
      if __name__ == "__main__":
          int2 = functools.partial(int, base = 2)
          print(int2('101', base = 8))
      

      此时输出的是65了,即八进制转为十进制。

    6.3 偏函数小总结

    • 当函数参数个数太多,我们需要简化是,我们不妨用上偏函数,使用functools.partial来创建一个新的函数,来固定住原函数中的某些参数,实现新的通用的功能。且用该创建方法更加的简洁,实则就是’节约代码行数‘啦。
  • 相关阅读:
    How to Create a site at the specified URL and new database (CommandLine Operation)
    Using Wppackager to Package and Deploy Web Parts for Microsoft SharePoint Products and Technologies
    SQL Server Monitor v0.5 [Free tool]
    How to build Web Part
    Deploy web part in a virtual server by developing a Web Part Package file(.cab)
    How to recreate "sites" link if you delete it accidentally
    SharePoint Portal Server管理匿名访问设置
    Monitor sql connection from .Net SqlClient Data Provider
    Brief installation instruction of Sharepoint Portal Server
    How to Use SharePoint Alternate URL Access
  • 原文地址:https://www.cnblogs.com/vanishzeng/p/12215986.html
Copyright © 2020-2023  润新知