zoukankan      html  css  js  c++  java
  • day13-三元表达式,生成式,递归函数,匿名函数

    其实学到这,函数部分的精髓你都已经学的差不多了,那今天的内容就再讲函数的其他方法使用介绍

    今天讲的内容有,三元表达式,列表生成式,字典生成式集合生成式以及生成器表达式(生成器生成式),还有递归函数和匿名函数.

    别看介绍这么多.其实除了递归函数有一点难运用,其他的都是和记用法一样.

    话不多说.进入今日主题

    一.三元表达式

    概念:其实三元表达式就是一个对流程控制if,else的优化.

    语法:一元 if 二元 else 三元

    其中一元指的是当if后面的条件,成立的情况下,返回的值.

    二元值的就是判断的条件

    三元值的就是当if后面的条件,不成立的情况下,返回的值

    返回的是一元或者三元的值

    例子:比较俩个数字的大小,并返回大的数字

    方法1:用函数

    def max2(x,y):
        if x > y:
            return x
        else:
            return y
    print(max2(111,432))  # 432
    

    方式2:用三元表达式

    x = 111
    y = 432
    res = x if x > y else y
    print(res)  # 432
    

    即三元表达式就一个结论:# 即if中间的为判断条件,左边的为条件成立返回的值,右边为条件不成立返回的值

    res1 = 'yes' if x > y else 'no'

    接下来就是一些生成式

    生成式

    列表生成式

    概念:在列表内做一些表达式操作,将返回的值添加到一个新列表

    语法:[i for i in range(10) if i < 6]

    • 中间为for循环执行的代码体
    • if 后面表示的是循环体内的一个if分支,一个条件判断,可选参数
    • 最前面的i表示的是要返回的值

    案例:将0-9添加到一个列表中

    方式1:平常的代码

    l = []
    for i in range(10):
        l.append(i)
    print(l)
    

    方式2:列表生成式

    l1 = [i for i in range(10)]
    print(l1)
    

    案例2:将0-9中大于6的添加到一个列表中

    方式1:平常的代码

    l2 = []
    for i in range(10):
        if i < 6:
            l2.append(l2)
    

    方式2:列表生成式,如何优雅的取代了forif

    l3 = [i for i in range(10) if i < 6]
    print(l3)
    

    案例3:将列表中的元素都加上'_sb'

    方式1:平常的代码

    names = ['song', 'liu', 'xiong']
    names1 = []
    for name in names:
        name = name + '_sb'
        names1.append(name)
    print(names1)
    

    方式2:列表生成式

    l4 = [name+"_sb" for name in names]
    
    print(l4)
    

    案例4:将列表中带_sb的名字打印,用列表生成式实现

    names2 = ['jkey', 'liu_sb', 'song_sb']
    names4 = [name for name in names2 if name.endswith("_sb")]
    print(names4)
    

    其实,你可以发现,列表生成式其实就是一个精简代码的一个内置方法.一般都是搭配for 循环使用

    那列表有生成器,其他的字典,元组,和集合呢??

    对他们都有对应的生成式.值得一提的是python没有元组表达式因为元组就是一个不可变的列表.

    如果你想要进行表达式 ,可以使用 list 和 tuple 之间的转换

    所以()在外面python就有了另外一层表达式的意思,即我们的生成器表达式

    那一步步来.让我们看看字典生成器怎么用

    字典生成式案例:

    dict1 = {f'k_{str(i)}': i**2 for i in range(10) if i > 3}
    print(dict1)  # {'k_4': 16, 'k_5': 25, 'k_6': 36, 'k_7': 49, 'k_8': 64, 'k_9': 81}
    

    还有集合生成器的使用

    集合生成器案例:

    set1 = {i for i in range(10) if i > 3}
    print(set1)  # {4, 5, 6, 7, 8, 9}
    

    和列表生成器的玩法差不多,所以大家看一下案例,知道怎么使用就可以了

    还是就是我们的生成器表达式

    生成器表达式(==>生成器生成式)

    也放俩个案例,来帮助大家理解吧

    案例1:生成器表达式的简单使用

    res = (i for i in range(3))  # 这里的()表示的就是一个生成器表达式
    print(res)  # 返回的类型是 生成器对象,即自定义的迭代器对象<generator object <genexpr> at 0x0000029056119CC8>
    
    # 那下面就可以使用next方法去遍历取值了
    print(next(res))  # 0
    print(next(res))  # 1
    print(next(res))  # 2
    print(next(res))  # StopIteration
    
    
    

    案例2:统计一个文件的字符长度

    with open('a.txt', mode='rt', encoding='utf-8') as f:
        # 方法1:之间将其全部读到内存.再计算其文件的长度
        res = f.read()
        print(len(res))
        # 方法2:用for循环一行一行的计算长度,并累加
        res = 0
        for line in f:
            res += len(line)
        # 方法3:用列表生成器进行上面的方式2操作
        res1 = sum([len(line) for line in f])
        # 进一步,用生成器
        res2 = sum((len(line) for line in f))
        # 俩层括号可以简化成一个,即
        res3 = sum(len(line) for line in f)
    

    那我们用生成器的一个好处是撒?是的那就是昨天讲的,节省内存空间,即这里因为是一个迭代器对象,所以它不会像列表(可迭代对象)一样,放的是一个个的值在内存中,而是一个可以产生值的对象,来一个就产生一个,要下一个值,上一个就以及在内存中释放了.

    是不是发现这些生成式很简单,只要记下怎么使用就好了.

    那接下来就来一个带点难度的

    递归函数

    概念:在调用一个函数的内部又调用自己,所以递归调用的本质就是一个循环的过程

    什么意思??循环??那我用while循环和for循环不可嘛

    别着急

    我们先来看看,递归函数长撒样

    def func():
        print("func")
        func()
    
    func()
    

    上面就是一个简单的递归函数,即在函数体内部执行函数本身.

    我们去执行看看

    返回结果:

    func
    func
    ....
    RecursionError: maximum recursion depth exceeded while calling a Python object
    

    欸,它会报错是不是

    那是因为我们只是让它执行了函数体本身,但是并没有结束条件啊,所以就相当于是一个死循环了.

    而为何这里会报错呢?

    是因为python以及为我们考虑到了,你可能会有一个死循环的函数.所以它给我们内部定义了一个可以循环的最大上限次数,一般为1000次.1000次过后则抛异常.为的就是优化内存

    可以通过sys模块下的getrecursionlimit,查看最大循环次数

    也可以通过setrecursionlimit来重新设置最大循环次数

    即下方代码

    import sys
    sys.setrecursionlimit(2000)  # 设置成最大次数为2000
    print(sys.getrecursionlimit())  # 查看最大循环的次数(默认为1000)
    

    所以我们递归函数有一个大前提:递归调用一定要在某一层结束

    递归其实是有俩个阶段的

    即回溯和递推

    1、回溯:向下一层一层挖井
    2、递推:向上一层一层返回

    下面来俩个案例.方便大家理解

    现在我有一个需求:

    我去问一个人的年龄,第一个人说我的年龄是比第二个人大10岁,然后我们又去问第二个人,第二个人说:我的年龄比第三个人的年龄大10岁,然后我们又去问第三个人,第三个人说.我的年龄比第四个人的大10岁....,n次后,第n个人说我的年龄是18岁,算出第一个人的年龄

    现在我们反向思维想一下,代码如下

    age(n) = age(n-1) + 10
    ....
    age(5) = age(4) + 10
    age(4) = age(3) + 10
    age(3) = age(2) + 10
    age(2) = age(1) + 10
    age(1) = 18
    

    欸,我们看到这不就是一个循环的过程嘛?

    我们是不是可以用递归函数试试?

    说了就干

    def age(n):  # n 表示有n个人
        if n == 1:  # 即是我们告诉了实际年龄的那个人
            return 18
        return age(n-1) + 10  # 这里表示执行的循环关系
    

    我们调用试试

    res = age(5)
    print(res)  # 58
    

    输出58 是不是没问题

    如果这个你没理解,没关系,我们还有案例

    当然这个案例,就更好的说明了递归函数使用的恰到好处

    案例需求: 一个列表 nums = [1,[2,[3,[4,[5,[6,[7,]]]]]]],我们取出列表的每一个值.

    我靠.这不是玩我们嘛?谁没事套这么多列表,是的,我就是玩你.来试着实现吧.

    我想我们大家一开始都会想到我们熟悉的for循环,对 for 循环是可以取出来.

    for循环代码

    for num in l:
        if type(num) is list:
            for num1 in num:
                if type(num) is list:
                    for num2 in num1:
                        ...
        else:
            print(num)
    

    我靠,这什么玩意?有几个列表我for几次???

    明显这不行.

    我们看看根本,我们是一个个拿到新的列表,每次都对新列表做一样的操作,但里面执行的明明是一样的,那个新列表来自的就是上一个的列表中的第二个值.

    所以这时候我们想到了我们的递归函数.

    代码示例:

    nums = [1,[2,[3,[4,[5,[6,[7,]]]]]]]
    
    def get(l):
        for num in l:
            if type(num) is list:
                get(num)  # 让他每一次来的都是一个新列表,这不久满足了我们的要求嘛
            else:
                print(num)
    
    get(nums)  # 1,2,3,4,5,6,7
    

    递归函数还有一个用处

    我们看看下面一个列表

    nums = [-3,1,3,7,13,23,37,43,57,63,77,91,103]

    我要看看一个数在不在这个列表里面

    很简单嘛

    find_num = 65
    for num in nums:
    	if nm == find_num:
    		print('you got it')
    		break
    else:
    	print('no exitc')
    

    但你想想如果你查找的值在列表的前面还好,就遍历了,几次,如果是最后面呢?我们是不是要遍历很多次?而且前面的都是无效的遍历.

    这时候,用我们的递归函数就有一个好处了.

    我们可以拿到中间的那个值,然后再判断,要查找的这个数,和中间这个数比较,大了的话,我就再在中间这个数的右边查找,小了的话,就走这个数的左边查找.依次类推,是不是就很大程度上加快了查询,这就是我们传说中的二分法

    来我们用代码实现:

    二分法

    nums = [-3,1,3,7,13,23,37,43,57,63,77,91,103]
    find_num = 64
    
    def find(nums,find_num):
        print(nums)
        if len(nums) == 0:  # 当这个列表被分为空了还没找到,那说明这个要查找的数不在这个列表中
            print("not exists")
            return  # 就不会往下执行了,解决了如果列表为空,还根据索引1取值的异常
        mid_index = len(nums) // 2  # 拿到列表的中间索引
        if find_num > nums[mid_index]:
            # in the right
            find(nums[mid_index+1:],find_num)
        elif find_num < nums[mid_index]:
            # in the left
            find(nums[:mid_index], find_num)
        else:
            print('you got it')
    
    
    find(nums,find_num)
    

    这个递归函数是不是很有意思.

    接下来,还有更有意思的.

    我们之前用的函数是不是都带一个名称的.例如 def func():pass 其中的func就是这个函数的名字.那我们是不是也可以有没有名称的函数呢?是的,它就是我们的匿名函数

    匿名函数 lambda

    概念:一个没有名字的函数,功能却和函数一样

    语法: lambda x,y:x + 2 * y

    • lambda 后面的 x,y 表示的是匿名函数的参数, :后面的表示的是函数的返回值

    特点:用于临时用一次的

    使用场景:一般搭配内置的函数使用.

    下面是匿名函数的俩种之间使用的案例:

    方法1:看完就忘(因为这样用我还不如用有名函数)

    d = lambda x,y:x + 2*y
    print(d)  # <function <lambda> at 0x000001CA0CBB7F78>
    res = d(1,2)
    print(res)  # 5
    

    方法2:可以这样用,但可读性差

    res = (lambda x,y:x + 2 * y)(1,2)
    print(res)  # 5
    

    所以还是之间来和我们的新朋友,几个内置函数一起来玩吧

    那就边写案例,边介绍吧

    案例1:下方有一个员工工资字典,我们根据工资的多少,分别将工资最高的和最低的员工姓名,以及给他们

    按照多到少,少到多的员工信息

    salaries = {
        "egon": 3000,
        "tom": 10000,
        'zxx': 1000
    }
    

    取出最高的,这里先介绍一下,max()这个内置函数的作用

    max函数

    功能:返回一个可迭代的对象的最大值

    语法: max(iteration, [key=func])

    • 其中iteration表示的是可迭代对象
    • key=func,可选参数,表示按照什么依旧来返回最大值
      • func为一个函数内存地址

    来加到我们的案例中

    def func(k):
        return salaries[k]  # 依据是字典的value即薪资
    
    
    print(max(salaries, key=func))  # 返回的是tom
    

    我们再将函数变为匿名函数试试

    print(max(salaries, key=lambda k: salaries[k]))  # 是等同于 print(max(salaries, key=func))
    

    ok,完成了需求1,返回最大薪资的员工姓名

    需求2:返回薪资最低的员工姓名

    先介绍min函数

    和max函数的使用方法一样,但是它的功能是返回一个可迭代的对象的最小值

    语法: max(iteration, [key=func])

    • 其中iteration表示的是可迭代对象
    • key=func,可选参数,表示按照什么依旧来返回最大值
      • func为一个函数内存地址

    加到我们案例中

    print(min(salaries, key=lambda k: salaries[k]))
    

    需求2也满足了

    需求3和需求4,返回他们的顺序,即从低到高和从高到低

    函数sorted

    功能:返回一个排序后的可迭代对象

    语法:max(iteration, [key=func],[reverse=False])

    • 其中iteration表示的是可迭代对象
    • key=func,可选参数,表示按照什么依旧来返回最大值
      • func为一个函数内存地址
    • reverse=False,表示默认的以升序排序

    放到案例中

    print(sorted(salaries, key=lambda k: salaries[k]))  # ['zxx', 'egon', 'tom']
    print(sorted(salaries, key=lambda k: salaries[k], reverse=True))  # ['tom', 'egon', 'zxx']
    

    ok,需求都完善了

    其他内置函数

    下面还有一些其他内置函数的玩法.大家可以试着敲敲看看

    # map
    res = list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))  # map()返回的是 <map object at 0x00000227FF6FEBC8>
    print(res)
    
    names = ["lxx", 'hxx', "wxx", 'lili']
    #                    规则
    #       "lxx_sb","hxx_sb"
    
    l = (name + "_sb" for name in names)
    res = map(lambda name:name + "_sb",names)
    print(list(res))
    
    # filter  #  过滤出某个元素
    names = ["lxx_sb",'egon',"wxx_sb",'lili_sb']
    print([name for name in names if name.endswith('sb')])
    
    res = filter(lambda name:name.endswith('sb'),names)
    print(list(res))
    
    # reduce # 拼接元素
    from functools import reduce
    
    res = reduce(lambda x,y:x+y,[1,2,3])
    res = reduce(lambda x,y:x+y,["aa","bb","cc"])
    print(res)
    
    
    res = pow(10,2,3)  # 等同于 10 ** 2 % 3  
    print(res)
    

    最后给大家来介绍一下什么是面向过程编程

    面向过程编程:

    ​ 核心是"过程"二字,过程就是解决问题的步骤.即先干啥,再干啥,后干啥
    ​ 所以基于该思想编写程序就好比设计一条一条的流水线

    优点:复杂的问题流程化,将一个大问题变成了一个个的小问题,进而就实现了简单化
    缺点:牵一发而动全身,扩展性差

    总结:

    1.三元表达式 xxx if xxx else xxx 第一个xxx 为第二个xxx成立时返回的结果,第三个xxx表示的是第二个xxx不成立时返回的结果

    2.生成式:[i for i in range(10) if i > 5] 这样一个模板实例

    3.递归函数:在函数内调用函数本身,一定要有结束条件,才有意义.

    4.匿名函数:lambda , lambda x,y:x+y,:前的为参数,后面的为返回值

    5.一些内置方法

    max()返回最大值
    min()返回最小值
    sorted()默认升序返回,当参数reverse=True时,为降序返回
    以上内置都有key=func的参数可选 表示的比较的依据,func为一个函数内存地址
    map() 映射,改变可迭代对象的值
    filter()  过滤
    reduce() 拼接元素
    
  • 相关阅读:
    HDU 5818 Joint Stacks
    HDU 5816 Hearthstone
    HDU 5812 Distance
    HDU 5807 Keep In Touch
    HDU 5798 Stabilization
    HDU 5543 Pick The Sticks
    Light OJ 1393 Crazy Calendar (尼姆博弈)
    NEFU 2016省赛演练一 I题 (模拟题)
    NEFU 2016省赛演练一 F题 (高精度加法)
    NEFU 2016省赛演练一 B题(递推)
  • 原文地址:https://www.cnblogs.com/jkeykey/p/14217874.html
Copyright © 2011-2022 走看看