zoukankan      html  css  js  c++  java
  • 十二、函数式编程:匿名函数、高阶函数、装饰器

    一、匿名函数

    即没有函数名的函数,又称lambda表达式。

    使用关键字lambda

    定义:lambda parameter_list:expression 其中parameter_list是参数列表,expression是简单表达式,不能是复杂的代码块。(x+y是表达式;a=x+y不是表达式是代码语句)

    特点:1)无函数名;2)无return关键字

    学习方法:对比普通函数

    例:

     1 #coding=utf-8
     2 
     3 def add(x,y):
     4     #普通函数
     5     return x+y
     6 
     7 f=lambda x,y:x+y#lambda表达式
     8 
     9 print(add(1,2))
    10 print(f(1,2))

    二、三元表达式

    即表达式版本的if...else

    例如:比较x和y,x大于y时取x,否则取y.

    其他语言的三元表达式:

    x>y?x:y

    python语言的三元表达式:

    伪代码如下

    条件为真时返回的结果 if 条件判断 else 条件为假时的结果

    x if x>y else y

    1 #coding=utf-8
    2 
    3 x=2
    4 y=3
    5 r=x if x>y else y
    6 print(r)

    lambda 后面非常适合三元表达式

    三、map

    推荐在python代码中多多使用

    map是一个类,定义 map(func, *iterables),其中func是方法名,*iterables是一个或多个列表(集合)

    适用场景:映射,如抛物线

    例如已知列表list_x=[1,2,3,4,5,6,7,8],求list_x中各元素的平方

    1、常规实现方法:

     1 # coding=utf-8
     2 
     3 def square(x):
     4     return x * x
     5 
     6 
     7 list_x = [1, 2, 3, 4, 5, 6, 7, 8]
     8 list_y = []
     9 for x in list_x:
    10     r = square(x)
    11     list_y.append(r)
    12 print(list_y)

    2、使用map类

    1)基础使用

    1 # coding=utf-8
    2 
    3 list_x = [1, 2, 3, 4, 5, 6, 7, 8]
    4 r = map(lambda x: x * x, list_x)
    5 # print(r)
    6 print(list(r))

    2)如果lambda有多个参数呢?例如求x*x+y

    1 # coding=utf-8
    2 
    3 list_x = [1, 2, 3, 4, 5, 6, 7, 8]
    4 list_y = [1, 2, 3, 4, 5, 6, 7, 8]
    5 
    6 r = map(lambda x, y: x * x + y, list_x, list_y)
    7 
    8 print(list(r))

    map(func, *iterables)中的*iterables是可变参数,支持多个列表

    注意,参数顺序要正确,例如下面的代码,打印结果不同

     1 # coding=utf-8
     2 
     3 list_x = [1, 2, 3, 4, 5, 6, 7, 8]
     4 list_y = [1, 1, 1, 1, 1, 1, 1, 1]
     5 
     6 r1 = map(lambda x, y: x * x + y, list_y, list_x)
     7 r2 = map(lambda x, y: x * x + y, list_x, list_y)
     8 
     9 print(list(r1))
    10 print(list(r2))

    打印结果

    [2, 3, 4, 5, 6, 7, 8, 9]
    [2, 5, 10, 17, 26, 37, 50, 65]
    3)特殊情况,如果两个列表长度不一致,结果如何?

    1 # coding=utf-8
    2 
    3 list_x = [1, 2, 3, 4, 5, 6, 7, 8]
    4 list_y = [1, 1, 1, 1]
    5 
    6 r = map(lambda x, y: x * x + y, list_x, list_y)
    7 
    8 print(list(r))

    打印结果

    [2, 5, 10, 17]

    两个列表元素个数要一致,如果不一致,结果取小的。

    六、reduce

    1、python3中的reduce不在全局命名空间,需要import导入

    from functools import reduce

    2、reduce是连续计算,会对参数序列中元素进行累积。

    3、reduce函数的定义:

        reduce(function, sequence[, initial]) -----> value

      function参数是一个有两个参数的函数,reduce依次从sequence中取一个元素,和上一次调用function的结果做参数再次调用function。 第一次调用function时,如果提供initial参数,会以sequence中的第一个元素和initial作为参数调用function,否则会以序列sequence中的前两个元素做参数调用function。

    代码一:基本使用

    1 #coding=utf-8
    2 from functools import reduce
    3 
    4 list_x=[1,2,3,4,5,6,7,8]
    5 r=reduce(lambda x,y:x+y,list_x)
    6 print(r)
    36

    计算过程

    每一次的计算结果作为reduce中lambda的参数

    (((((1+2)+3)+4)+5)+6)+7)+8=36

    实现了序列求和

    代码二:有初始值

    1 #coding=utf-8
    2 from functools import reduce
    3 
    4 list_x=[1,2,3,4,5,6,7,8]
    5 # r=reduce(lambda x,y:x+y,list_x)
    6 #含初始值10
    7 r=reduce(lambda x,y:x+y,list_x,10)
    8 
    9 print(r)

    运行结果

    46

    问题:用reduce计算旅行者坐标,起点(0,0),移动列表[(1,3),(2,-2),(-2,3)],计算旅行者的最终位置

    七、filter过滤器

    1、filter函数会对指定序列执行过滤操作。

    2、filter函数的定义:

        filter(function or None, sequence) ----> list, tuple, or string

      function是一个谓词函数,接受一个参数,返回布尔值True或False。

      filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素。

      返回值的类型和参数sequence的类型相同

    3、代码示例一

     1 #coding=utf-8
     2 
     3 '''过滤掉列表中的0'''
     4 list_x=[1,0,2,0,3,0,4,0]
     5 
     6 #lambda必须返回一个可以判断真假的结果,r中的返回True或False
     7 r=filter(lambda x:True if x!=0 else False,list_x)
     8 #r1中的lambda返回非0
     9 r1=filter(lambda x:x,list_x)
    10 print(list(r))
    11 print(list(r1))

    代码示例二

    #coding=utf-8
    
    '''过滤掉列表中小写字母'''
    list_n=['a','B','C','f','e']
    r=filter(lambda x:True if x.isupper() else False,list_n)
    print(list(r))
    "C:Program FilesPython36python3.exe" E:/pyClass/eleven/c16.py
    ['B', 'C']

    八、命令式编程VS函数式编程

    命令式编程即过程控制,主要关键字:def if ..else  for

    函数式编程,必须关键字:三个类map reduce filter,一个表达式lambda

    lambda是函数式编程的最基本单元

    lisp语言是函数式编程的鼻祖

    python支持函数式编程,但本质还是命令式编程,适当使用可使代码简洁。

    九、装饰器一

    很有用,并非python独有,如java中的注释。

    1、什么是装饰器

    2、适用场景

    3、能解决什么问题

     1 #coding=utf-8
     2 
     3 '''1)原有函数f1,实现打印功能'''
     4 # def f1():
     5 #     print("this is a function")
     6 
     7 
     8 '''
     9 2)新需求:现在要在f1中增加打印时间戳的功能
    10 '''
    11 import time
    12 #
    13 # def f1():
    14 #     print(time.time())
    15 #     print("this is a function")
    16 #
    17 # #调用
    18 # f1()
    19 
    20 '''
    21 3)新需求:有多个函数,要求在每个函数中增加打印时间戳的功能
    22 原则:开闭原则告诉我们要对扩展开发,对修改关闭;
    23 尽量不修改原有函数的定义和实现,而是通过扩展实现新的需求
    24 '''
    25 #定义一个打印时间戳的函数
    26 def f1():
    27     print("this is a function f1")
    28 
    29 def f2():
    30     print("this is a function f2")
    31 
    32 def print_current_time(func):
    33     print(time.time())
    34     func()
    35 
    36 print_current_time(f1)
    37 print_current_time(f2)

    运行结果

    "C:Program FilesPython36python3.exe" E:/pyClass/eleven/c17.py
    1532585365.6654336
    this is a function f1
    1532585365.6654336
    this is a function f2

    以上代码虽然满足需求:没有修改原来的函数,实现了新需求,但是只是在每个函数之前增加了一句打印时间戳的语句,并没有提醒和原有函数的关联性,其本质和下面的代码没有区别。

     

    1 print(time.time())
    2 f1()
    3 print(time.time())
    4 f2()

    十、装饰器二

     1 #coding=utf-8
     2 
     3 import time
     4 
     5 '''
     6 使用闭包,定义一个装饰器(decorator),内部嵌套被封装(wrapper)函数
     7 def decorator():
     8 
     9     def wrapper():
    10         pass
    11     return wrapper
    12 '''
    13 def f1():
    14     print("this is a function f1")
    15 
    16 #定义装饰器
    17 def decorator(func):
    18     def wrapper():
    19         print(time.time())
    20         func()
    21     return wrapper
    22 
    23 #调用
    24 f=decorator(f1)
    25 f()

    十一、装饰器三

    上面的代码,定义复杂,调用也复杂。我们可以接受定义复杂,但不能接受调用复杂。那有没有什么方式可以不用改变调用方式,来实现呢?

    就是使用语法糖,关键字@

     1 #coding=utf-8
     2 import time
     3 
     4 #定义装饰器
     5 def decorator(func):
     6     def wrapper():
     7         print(time.time())
     8         func()
     9     return wrapper
    10 
    11 #使用语法糖
    12 @decorator
    13 def f1():
    14     print("this is a function f1")
    15 
    16 #原有调用方式不变
    17 f1()

    运行结果

    "C:Program FilesPython36python3.exe" E:/pyClass/eleven/19.py
    1532586681.7921555
    this is a function f1

    十二、装饰器四

    上面讲的被装饰的函数是不带参数的,如果f1()带参数呢?

    解决方式很简单,只要在定义装饰器的内部函数wrapper()中增加参数即可,调用方式不变

    代码如下:

     1 #coding=utf-8
     2 import time
     3 
     4 #定义装饰器
     5 def decorator(func):
     6     def wrapper(func_name):
     7         print(time.time())
     8         func(func_name)
     9     return wrapper
    10 
    11 #使用语法糖
    12 @decorator
    13 def f1(func_name):
    14     print("this is a function "+func_name)
    15 
    16 #原有调用方式不变
    17 f1('f1')

    打印结果

    "C:Program FilesPython36python3.exe" E:/pyClass/eleven/c20.py
    1532587192.487322
    this is a function f1

    进一步,如果被装饰的函数有多个,且每个函数的参数个数也不同,如f1()有一个参数,f2()有两个参数,f3()有三个参数。。。该如何实现?

    装饰器需要具有通用性,任何类型的函数都可以使用,那么可变参数可以解决这个问题。

    代码如下:

     1 #coding=utf-8
     2 import time
     3 
     4 #定义装饰器
     5 def decorator(func):
     6     def wrapper(*args):
     7         print(time.time())
     8         func(*args)
     9     return wrapper
    10 
    11 #使用语法糖
    12 @decorator
    13 def f1(a):
    14     print("this is a function "+a)
    15 
    16 #使用语法糖
    17 @decorator
    18 def f2(a,b):
    19     res=a+b
    20     print(res)
    21     
    22 
    23 #原有调用方式不变
    24 f1('python')
    25 print(f2(1,3))

    十三、装饰器五

    如果f3()中含有关键字参数呢?

    1 def f3(a,b,**kw):
    2     print(a)
    3     print(b)
    4     print(kw)

    在内层函数wrapper()中增加**kw参数

    代码实现如下

    #coding=utf-8
    import time
    
    #定义装饰器
    def decorator(func):
        def wrapper(*args,**kw):
            print(time.time())
            func(*args,**kw)
        return wrapper
    
    #使用语法糖
    @decorator
    def f1(a):
        print("this is a function "+a)
    
    #使用语法糖
    @decorator
    def f2(a,b):
        print(a)
        print(b)
    
    @decorator
    def f3(a,b,**kw):
        print(a)
        print(b)
        print(kw)
    
    
    #原有调用方式不变
    f3('test1','test2',x=1,y=2,z=3)

    运行结果

    "C:Program FilesPython36python3.exe" E:/pyClass/eleven/c20.py
    1532588529.5278778
    test1
    test2
    {'x': 1, 'y': 2, 'z': 3}

    小tips:func(*args,**kw)通杀所有函数

    十四、装饰器六

    1、解决什么问题:

    装饰器实际上就是为了给某程序增添功能,但又不修改源代码,满足以下三个条件:

    1)不能修改被装饰的函数的源代码

    2)不能修改被装饰的函数的调用方式

    3)满足1)2)的情况下给程序增添功能

    2、适用场景

    • 1.可以在外层函数加上时间计算函数,计算函数运行时间;
    • 2.计算函数运行次数;
    • 3.可以用在框架的路由传参上;
    • 4.插入日志,作为函数的运行日志;
    • 5.事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败;
    • 6.缓存,实现缓存处理;
    • 7.权限的校验,在函数外层套上权限校验的代码,实现权限校验;
  • 相关阅读:
    接口测试入门(5)----新框架重构,使用轻量级的HTTP开发库 Unirest
    接口测试入门(4)--接口自动化测试框架 / list和map用法 / 随机选取新闻 (随机数生成) / 接口相关id映射
    接口测试入门(3)--使用httpClient进行登录用例操作/set-cookies验证/ List<NameValuePair>设置post参数/json解析
    接口测试入门(2)--get和post初级请求/使用httpclient做一个获取信息list的请求(需要登录才可以)
    接口测试学习入门(1)--前期知识储备
    j2ee 使用db.properties连接mysql数据库
    com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别 serverTimezone设定
    mysql JDBC URL格式各个参数详解
    mysql新建数据库时的collation选择(转)
    SpringBoot MyBatis 配置多数据源 (静态多个)
  • 原文地址:https://www.cnblogs.com/loveapple/p/9364339.html
Copyright © 2011-2022 走看看