python3 三元运算,列表解析,生成器表达式

什么是生成器,本质上就是我们自己写的函数

可以理解为一种数据类型,自动实现了迭代器协议,所以生成器就是可迭代对象
#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码,只有在调用.__next__ 方法的时候再执行函数内部代码
生成器函数:常规函数定义,但是使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在这个结果中间挂起函数的状态,以便下次从它离开的地方继续执行

def test():
    yield 1
    yield 2
    yield 3
g=test()
print('来自函数',g)

运行结果:

来自函数 <generator object test at 0x0000000002812F10>

判断它是否是一个迭代器?

print(isinstance(g,Iterable))
print(isinstance(g,Iterator))

运行结果:

True
True

由此可见,生成器就是一个迭代器,可以按照迭代器的方法进行取值

def test():
    print('a')
    yield 1
    print('b')
    yield 2
    print('c')
    yield 3
g=test()
print(g.__next__())

运行结果:

a
1

#由此可见,只有在调用.__next__方法的时候才会执行函数内部的代码,而且碰到yield之后就会停止,函数状态就会保留在yield那里,等待下一次的调用

但是注意了,取生成器的值除了.__next__之外,还有send也可以取,我们看一个例子:

def generator():
    print(123)
    content=yield 1
    print('=====',content)
    print(456)
    age=yield 2
g=generator()
ret=g.__next__()
print('***',ret)
ret=g.send('hello')
print('***',ret)

运行结果:

123
*** 1
===== hello
456
*** 2

#为什么多了个hello出来呢?

#像我们之前说的,生成器函数遇到yield之后就会停止继续往下执行,在函数中,先运行=号右边的,再执行=号左边的,

#因为content=yield 1 执行到了 yield 1 之后就不再执行了,所以content=None,然后再进行send(‘hello’)的时候就将hello传给了content,所以这就是为什么多了一个hello的原因

#特别要注意一点,yield应该作为函数的结尾,如果在yield 后面再加代码,执行.__next__就会有问题

#在send前面必须要调用一次.__next__ 

例子:移动取平均值

#每传一个就计算一次平均值,可以利用send

def avger():
    sum=0
    avg=0
    count=0
    while True:
        num=yield avg
        sum+=num
        count+=1
        avg=sum/count
ret=avger()##利用avger生成器函数生成一个生成器
ret.__next__()
avg1=ret.send(10)#接受生成器的返回值
avg1=ret.send(20)
print(avg1)

运行结果:

15.0

#预激生成器的装饰器

主要的作用就是不需要生成器自己调用.__next__

def init(func):
    def inner(*args,**kwargs):
        g=func(*args,**kwargs)
        g.__next__()
        return g
    return inner
@init
def avger():
    avg=0
    count=0
    sum=0
    while True:
        num=yield avg
        sum+=num
        count+=1
        avg=sum/count

avg1=avger()#产生一个生成器,因为已经预激活了,所以直接就可以send了,不需要像之前加 avg1.__next__()
ret=avg1.send(10) 
ret
=avg1.send(20)
print(ret)

 如何循环两个字符串:

def test():
    a='abcdefg'
    b='1234567'
    yield from a
    yield from b
a=test()
for i in a:
    print(i )

 迭代器作业:处理文件,用户指定要查找的文件和内容,将文件中包含要查找内容的,每一行都输出到屏幕

def check_file(filename,aim):
    with open(filename,encoding='utf-8') as f:
        for line in f:
            if aim in line :
                yield line
check=check_file('filetest.txt','zhen')
for i in check:
    print(i)
写生成器,从文件中读取内容,在每一次读取到内容之前加上
*** 之后再返回给用户
def check_file(filename):
    with open(filename,encoding='utf-8') as f:
        for line in f:
            yield '***'+line
check=check_file('filetest.txt')
for i in check:
    print(i)

 

生成器表达式: 类似于列表推导

三元表达式:

返回值在条件前面 'SB' if name == 'children' else '帅哥'

name='children'
res= 'SB' if name == 'alex' else '帅哥'
print(res)

运行结果
SB

列表解析:

一个正常的for循环函数

egg_list=[]
for i in range(10):
    egg_list.append('鸡蛋%s' %i)
print(egg_list)

但是这个函数可以一条语句写完,这个就是列表解析

egg=['鸡蛋%s' %i for i in range(10)]
print(egg)

注意格式:输出内容在前,然后循环在后

列表解析也可以套用三元表达式

egg=['鸡蛋%s' %i for i in range(10) if i >5 ]
print(egg)

输出i大于5 的
['鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

例一:30以内所有能被3整除的数

count=[i for i in range(30) if i%3==0]
print(count)

运行结果:

[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

例二:30以内所有能被3整除的数的平方

count=[i**2 for i in range(30) if i%3==0]
print(count)

运行结果:

[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

例三:找到嵌套列表中名字含有两个‘e’的所有名字

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
num=[i for name  in names for i in name if i.count('e')==2 ]
print(num)

运行结果:

['Jefferson', 'Wesley', 'Steven', 'Jennifer']

字典推导式

例一:将一个字典的key和value对调

mcase = {'a': 10, 'b': 34}
mcase_dic={mcase[k]:k for k in mcase}
print(mcase_dic)

运行结果:

{10: 'a', 34: 'b'}

例二:合并大小写对应的value值,将k统一成小写

mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
print(mcase_frequency)

运行结果:

{'a': 17, 'b': 34, 'z': 3}

集合推导式

例:计算列表中每个值的平方,自带去重功能

squared = {x**2 for x in [1, -1, 2]}
print(squared)

 

 

例1:  过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
li=['asdasda','asd','da','da2ea','dd','aasda']
count=[i for i in li if len(i)>=3]
print(count)
运行结果:
['asdasda', 'asd', 'da2ea', 'aasda']

例2: 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
count=[(x,y) for x in range(5) if x%2==0 for y in range(5) if y%2!=0]
print(count)
运行结果:
count=[(x,y) for x in range(5) if x%2==0 for y in range(5) if y%2!=0]
print(count)

例3: 求M中3,6,9组成的列表 M = [[1,2,3],[4,5,6],[7,8,9]]
M = [[1,2,3],[4,5,6],[7,8,9]]
count=[i for li in M for i in li if i in [3,6,9]]
print(count)

列表解析如何和生成器结合起来?

把[]改为()即可

laomuji=('鸡蛋%s' %i for i in range(10))

这样就可以迭代了
这样的就叫生成器表达式

生成器只能遍历一次

如何从生成器取值呢?

for循环

.__next__,

 

总结:
把列表解析的[]换成()就是生成器表达式了
列表解析与生成器表达式都是一种遍历的编程方式,但是生成器表达式更节省内存
Python 不但使用迭代器协议,让for循环变得更加通用。大部分内置函数也是使用迭代器访问对象的。例如sum函数,map函数,reduce函数

print(sum(x**2 for x in range(4))) #不需要先生成列表,节省内存空间
而不用多次一句先构造一个列表
print(sum([x**2 for x in range(4)]))#因为先要生成列表,所以需要更大的内存

文件监控例子:

#方法一:
f=open('filetest.txt','r',encoding='utf-8')
while True:
    line=f.readline()
    if line:
        print(line.strip())
#方法二:函数形式
def tail(filename):
    f=open(filename,encoding='utf-8')
    while True:
        line=f.readline()
        if line:
            print(line.strip())
tail('filetest.txt')
但是这样就会一直占用内存空间,浪费内存
而且如果打印结果不一样就无法满足了
比如A想在打印前面加****
B想在打印后面加——————
这样就无法满足了,这个时候就可以用yeid
为什么不用return呢?因为return 代表着函数结束
而且用还可以做定向输出,只打印自己想要的
#方法三
def tail(filename):
    f=open(filename,encoding='utf-8')
    while True:
        line=f.readline()
        if line.strip():
            yield line.strip()
g=tail('filetest.txt')
for i in g:
    if 'chao' in i :
        print('***',i)

 生成器面试题:

def demo():
for i in range(4):
yield i

g=demo()

g1=(i for i in g)
g2=(i for i in g1)

print(list(g1))
print(list(g2))
打印结果:

[0, 1, 2, 3]
[] 

为什么g2为空呢?因为生成器只能取一次值,取完之后就为空了

虽然g2是for 循环g1 但是g1是for循环g的,所以g的值被g1取完之后,g2再取就为空了

 

转载于:https://www.cnblogs.com/children92/p/9240747.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值