day19:生成器、yield表达式、三元表达式、生成式和函数的递归

一、生成器

1. 如何得到自定义的迭代器

【方法一】:在函数内一旦存在yield关键字,调用函数并不会执行函数体代码,会返回一个生成器对象,生成器即自定义的迭代器
【方法二】:生成器表达式

反问:为什么自定义的迭代器叫做生成器 ???
答:这是 规定 !!!

'''案例说明:'''

def func():
    print('第一次运行:')
    yield 1
    print('第二次运行:')
    yield 2
    print('第三次运行:')
    yield 3

g = func() # 存在yield,调用函数func时并没有执行函数,而是得到了一个生成器对象
print(g)  # <generator object func at 0x0000029CDF636030>

'''2、解释生成器即为迭代器'''
#注意看下面的调用,g这个生成器对象有__iter__()和__next__()方法,说明g也是迭代器对象,印证了“生成器即自定义的迭代器”这句话
# g.__iter__()
# g.__next__()

'''3、函数调用'''
# g.__next__()    # 仅仅取一个值,遇到yield时鼠标光标会停留在yield返回值的后面,这里是停在了 1 后面;
# print(g.__next__())   # 运行这一行时注释g.__next__(),print(g.__next__())输出值为2;
def func():
    print('第一次运行:')
    yield 1
    print('第二次运行:')
    yield 2
    print('第三次运行:')
    yield 3

g = func()
g.__next__()   # 第一个yield返回值没有被接收和打印
#>输出:第一次运行:
print(g.__next__())    # 第二个yield返回值有被接收和打印
#>输出:
#    第二次运行:
#    2

2. 函数本身方法的使用,例如g.next(),等价于netx(g)

len('aaa') # 等价于 'aaa'.__len__()

next(g)    # 等价于 g.__next__()
iter(可迭代对象)     #等价于 可迭代对象.__iter__()

3. 总结yield

有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值

二、yield表达式

1、x =yield ,后面不接返回值,即返回None

x = yield None

2、注意:给生成器传值,实际上是传给了yield,yield再将接收的值赋值给x

'''案例:学习函数体有yield关键字时的代码执行逻辑'''
def people(name):
    print('大飞飞%s准备吃东西啦...' %name)
    while True:
        x = yield None
        print('大飞飞%s吃了 %s' %(name,x))

g = people('lulucy')  # 得到了一个迭代器对象,因为函数体有yield,所以也叫做生成器
print(g)   # 输出:<generator object dog at 0x000001D33ACD5F50>

res = next(g)   # 第一次执行next或send,是触发函数体的运行,执行到x=yield时鼠标光标会移动到x=yield正后方,返回值为None(打印res可知),等待生成器再一次传值,代码被挂起
print(res)  # 获取到yield后面的返回值None

print('=========================')

next(g)  # 触发代码继续运行,先执行“print('大飞飞%s吃了 %s' %(name,x))”,执行到下一个循环到yield时代码再次挂起,等待下一次传值
g.send('鸡公煲')  # yield接收到的值为“鸡公煲”,此时将“鸡公煲”赋值给了x,出发代码继续运行,再次执行“print('大飞飞%s吃了 %s' %(name,x))”
g.send('三鲜煲')
g.close()        # 关闭生成器对象,后面无法继续传值
g.send('1111')   # 继续传值报错,StopIteration

三、三元表达式

【语法格式】: 条件成立时要返回的值 if 条件 else 条件不成立时要返回的值

1.举例:需求为比较两个数大小,引出三元表达式

'''案例:比较两个数大小'''

def func(x,y):
    if x > y:
        return x
    else:
        return y

res=func(1,2)
print(res)

2.使用三元表达式简化代码

def func(x, y):
    res = x if x > y else y
    return res

print(func(11,22))

四、生成式

4.1 列表生成式

'''需求:筛选出含有dsb的名字'''

l = ['alex_dsb', 'lxx_dsb', 'wxx_dsb', "xxq_dsb", 'egon']
new_l=[]

'''常规写法'''
for name in l:
    if name.endswith('dsb'):
        new_l.append(name)
print(new_l)

'''列表表达式'''
#筛选出含有dsb的名字
new_l = [name for name in l if name.endswith('dsb')]
print(f'列表表达式写法输出结果:{new_l}')

'''循环列表取值'''
new_l=[name for name in l] #  # 等价于 new_l = [列表.append(name) for name in l]
print(f'列表表达式遍历列表   :{new_l}')

# 把所有小写字母全变成大写
new_l = [name.upper() for name in l]
print(f'把所有小写字母全变成大写:{new_l}')

# 把所有的名字去掉后缀'_dsb'
new_l = [name.replace('_dsb', '') for name in l]
print(f'把所有的名字去掉后缀"_dsb":{new_l}')

4.2 字典生成式

keys=['name','age','gender']
dic={key:None for key in keys} # 将遍历出的列表值传给key,这里不加条件默认是true ==>等价于dic={key:None for key in keys if true},
print(dic) # {'name': None, 'age': None, 'gender': None}

'''去掉列表中含有gender的项'''
items=[('name','egon'),('age',18),('gender','male')]
res={k:v for k,v in items if k != 'gender'}  # for循环先取出一个个元组,然后解压赋值给k和v,如果k不等于gender,则解压赋值传给k:v
print(res) # {'name': 'egon', 'age': 18}

4.3 集合生成式

keys=['name','age','gender']
set1 = {k for k in keys} # 等价于set1 = {集合.append(k) for k in keys }
print(set1, type(set1))  # {'name', 'age', 'gender'} <class 'set'>

4.4 生成器表达式

创建一个生成器对象有两种方式,一种是调用带yield关键字的函数,另一种就是生成器表达式

'''错误案例:无元组生成式,小括号是生成器表达式'''
#说明:元组没有append用法,所以不存在元组生成式

g = (i for i in range(1,10)) # 等价于 g = (元组.append(i) for i in range(1,10))
print(g)  # <generator object <genexpr> at 0x0000013BD06145F0>
'''正确案例:生成器表达式'''

g = (i for i in range(10))

# !!!!!!!!!!!强调!!!!!!!!!!!!!!!
# 此刻g内部一个值也没有
print(g) # 得到一个生成器对象,<generator object <genexpr> at 0x7f1766c90660>

print(g.__next__()) # 0
print(g.__next__()) # 1
print(next(g))      # 2
print(next(g))      # 3
print(next(g))      # 抛出异常StopIteration

结论:如果range()里面是一个无穷大的数,for i in range(无穷大) 打印x的话会很占内存,对比列表生成式,生成器表达式的优点自然是节省内存(因为一次只产生一个值在内存中),如果没值则抛出StopIteration异常

思考:读取大文件内容,是一行行读取然后存到列表还是使用生成器读?

需求:读取文件,计算出文件中一共有多少个字符

案例:分别使用常规写法、列表表达式、生成器表达式读取文件中所有的字符长度总和

须知:sum()内置函数,传入的需要是可迭代对象
在这里插入图片描述

'''案例:传入sum里面的是可迭代对象,'''
# print(sum(1,2,3,4))  # TypeError: sum() takes at most 2 arguments (4 given)

print(sum([1,2,3,4,5,6])) # 21
'''案例说明'''

with open('aaa.txt', mode='rt',encoding='utf-8') as f:
# # 方式一:常规写法
    res=0
    for line in f:
        res+=len(line)
    print(f'方式一:{res}')

with open('aaa.txt', mode='rt',encoding='utf-8') as f:
# # 方式二:使用列表表达式,缺点是如果行数过多,列表里面的数也会过多,占内存
    res1 = 0
    res1 = sum([len(line) for line in f])
    print(f'方式二:{res1}')

with open('aaa.txt', mode='rt',encoding='utf-8') as f:
# # 方式三:使用生成器表达式,最简洁
    res2 = 0
    # g =(len(line) for line in f)   # 得到了一个可迭代对象
    # print(g)  # <generator object <genexpr> at 0x0000022A97EB45F0>
    
    # res2 = sum((len(line) for line in f)) # 等价于下方
    sum(len(line) for line in f)  # 等价于sum(g)
    print(f'方式三:{res2}')

五、函数的递归

一:递归的定义

函数的递归调用:是函数嵌套调用的一种特殊形式
具体是指:在调用一个函数的过程中又直接或者间接地调用到本身

递归算法:
1.函数直接或者间接调用自身
2.必须具有递归结束条件
3.每一次递归,问题规模缩小
4.python限制了递归深度(sys.getrecursionlimit())

5.1 直接调用本身

def f1():
    print('是我是我还是我')
    f1()
f1()

5.2 间接接调用本身

def f1():
    print('===>f1')
    f2()

def f2():
    print('===>f2')
    f1()

f1()

5.3 一段代码循环运行的两种方案

方式一:while、for循环
while True:
    print(1111)
    print(2222)
    print(3333)
方式二:递归的本质就是循环:
def f1():
    print(1111)
    print(2222)
    print(3333)
    f1()
f1()

二:需要强调的的一点

【注】:递归调用不应该无限地调用下去,必须在满足某种条件下结束递归调用

n=0
while n < 10:
    print(n)
    n+=1


def f1(n):
    if n == 10:
        return
    print(n)
    n+=1
    f1(n)

f1(0)

三:递归的两个阶段

回溯:一层一层调用下去(注:回溯即递归调用,终止回溯即终止递归调用)
递推:满足某种结束条件,结束递归调用,然后一层一层返回

# 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):
    if n == 1:
        return 18
    return age(n-1) + 10


res=age(5)
print(res)

在这里插入图片描述

四:递归的应用

'''案例1:列表l中再叠加1层列表'''

l = [1,2,3,[4,5]]

for i in l:
    if type(i) ==list:
        for a in i:
            if type(a) ==list:
                pass
                # ..... 如果迭代很多层怎么办?我们发现重复执行的代码即函数本身,重复调用函数本身
                # .....
            else:
                print(a)
    else:
        print(i)
'''案例2:叠加很多层'''

l=[1,2,[3,[4,[5,[6,[7,[8,[9,10,11,[12,[13,]]]]]]]]]]

def f1(list1):
    for x in list1:
        if type(x) is list:
            # 如果是列表,应该再循环、再判断,即重新运行本身的代码
            f1(x)
        else:
            print(x)

f1(l)
  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值