Python-列表推导式&生成器

列表推导式

格式:

list1 = [生成元素的表达式 for 原列表的元素 in 原列表 if 需要满足的条件]

使用:

以下面代码为例子,可以看到最终的list1里存了所有list0中的偶数元素的平方。
其运行的过程是,从list0中挨个取出元素存在变量i,然后判断i是否被2整除,如果被整除就执行最开始的表达式,即i * i,然后把这个元素放到新的列表中
可以看做,列表推导式就是从旧的列表之中推出新的列表

list0 = [2, 5, 6, 7, 4]
list1 = [i * i for i in list0 if i % 2 == 0]
print(list1)
'''
输出:
[4, 36, 16]
'''

列表推导式还有很多多其他的用法,下面举几个例子:
使用三元运算符

list0 = [2, 5, 6, 7, 4]
list1 = [i * i if i % 2 else -i for i in list0]
print(list1)
'''
输出:
[-2, 25, -6, 49, -4]
'''
# 这里Py的非零整数就是True,零就是False

对字符串元素操作

list0 = ['abc', 'ccc', 'here', 'cute']
list1 = [i * 5 for i in list0 if len(i) > 3]
print(list1)
'''
输出:
['hereherehereherehere', 'cutecutecutecutecute']
'''

过滤列表元素

list0 = ['abc', 'ccc', 3, 'here', 'cute', 5, True, False]
list1 = [i for i in list0 if isinstance(i, str)]
print(list1)
'''
输出:
['abc', 'ccc', 'here', 'cute']
'''

对字典元素进行操作

list0 = [{
    
    'key': 10}, {
    
    'key': 34}, {
    
    'key': 66}]
list1 = [i for i in list0 if i['key'] <= 10]
print(list1)
'''
输出:
[{'key': 10}]
'''

循环嵌套

list1 = [(i, j) for i in range(5) if i % 2 for j in range(5) if j % 2 == 0]
print(list1)
'''
输出:
[(1, 0), (1, 2), (1, 4), (3, 0), (3, 2), (3, 4)]
'''

列表推导式在运行效率上比遍历列表一个一个再加入新列表要更快。

集合推导式

其实和列表推导式一样,只不过多了去重而已

set1 = {
    
    i % 2 for i in range(10)}
print(set1)
''''
输出:
{0, 1}
''''
字典推导式

和列表推导式一样,不过会根据key进行覆盖,后生成的value会替换先生成的value

dict0 = {
    
    'aaa': 233, 'bbb': 332, 'ddd': 555}
dict1 = {
    
    key: value + 5 for key, value in dict0.items()}
print(dict1)
'''
输出:
{'aaa': 238, 'bbb': 337, 'ddd': 560}
'''

反转key和value
可以发现,反转后有重复的key,value就会被替换

dict0 = {
    
    'aaa': 233, 'bbb': 233, 'ddd': 555}
dict1 = {
    
    value: key for key, value in dict0.items()}
print(dict1)
'''
输出:
{233: 'bbb', 555: 'ddd'}
'''

生成器

生成器-表达式

如果把上面的列表推导式子的符号换成圆括号,那么新生成得的就是一个生成器了:
从下面的代码可以看出,list不再是一个列表,而是一个generator对象,当然仍然可以强制把它转成list

list1 = (i * i for i in range(6))
print(list1)
'''
输出:
<generator object <genexpr> at 0x0000013E4C03F5C8>
'''

此时,如果想要获取元素则需要使用next方法

generator = (i * i for i in range(6))
print(generator.__next__()) # 使用自带的__next__()方法来返回下一个元素
print(generator.__next__())
'''
输出:
0
1
'''
generator = (i * i for i in range(6))
print(next(generator))# 使用系统自带的next函数
print(next(generator))
print(next(generator))
'''
输出:
0
1
4
'''

需要注意的是,两种next每次只返回下一个元素,不能跳过某个元素或者从任意点开始返回,且生成器属于可迭代类型。
遍历生成器

generator = (i * i for i in range(6))
for i in generator:
    print(i)
'''
输出:
0
1
4
9
16
25
'''

使用map映射

generator = (i * i for i in range(6))
print(list(map(lambda x: x * x, generator)))
'''
输出:
[0, 1, 16, 81, 256, 625]
'''
生成器-函数

使用表达式只能创建简单的表达式,如果复杂一点就需要使用函数了

yield关键字

通过以下的代码,可以看到函数内加了yield关键字后,函数就变成了一个生成器

def generator(n: int):
    cnt = 1
    while cnt < n:
        cnt += n
        yield cnt


print(generator(5))
'''
输出:
<generator object generator at 0x0000024845B154C8>
'''

从下面的代码可以知道程序大概的流程(建议debug一遍看流程)

  1. ge获取到生成器对象的地址,但没有执行生成器内部的代码
  2. 执行第一个next,开始执行内部的代码,先是执行最外层初始化cnt,然后进入循环,直到执行到yield关键字,把cnt当做当次获取的值返回了出去,然后生成器内部的执行停止
  3. 执行第二个next,可以发现打印了‘yield后’说明第二次执行是接着上次yield结束的位置执行的
def generator(n: int):
    cnt = 1
    print("进入生成器")
    while cnt < n:
        cnt += 1
        print("yield之前")
        yield cnt
        print("yield后")


ge = generator(5)
print("得到生成器----开始获取元素", end='\n\n\n')
print(ge.__next__())
print("第一个元素获取完毕", end='\n\n\n')
print(ge.__next__())
print("第二个元素获取完毕", end='\n\n\n')
'''
输出:
得到生成器----开始获取元素


进入生成器
yield之前
2
第一个元素获取完毕


yield后
yield之前
3
第二个元素获取完毕

'''

得出结论,yield后面的变量会被当做值返回出去,并且遇到yield后,生成器内部的代码就停止执行,直到调用下一个next。再举一个例子:

def generator(n: int):
    cnt = 1
    print("进入生成器")
    cnt += 1
    yield cnt
    yield 23333


ge = generator(5)
print(ge.__next__())
print(ge.__next__())
'''
输出:
进入生成器
2
23333
'''

可以把yield看做一个特殊的return,不过执行完之后不会结束当前迭代器,yield其实也可以不跟返回的值这样yield就可以当成一个暂停的工具使用。

函数生成器与return

执行下面的代码会出现异常,因为当前的生成器只能生成两个元素,但是却执行了三个next

def generator(n: int):
    cnt = 1
    cnt += 1
    yield cnt
    yield 23333


ge = generator(5)
print(ge.__next__())
print(ge.__next__())
print(ge.__next__())
'''
輸出:
2
23333
Traceback (most recent call last):
  File "D:\Pycharm\Project\Pytest\test.py", line 11, in <module>
    print(ge.__next__())
StopIteration
'''

如果这里加一个return呢,可以发现return的值就被当做异常的消息输出了

def generator(n: int):
    cnt = 1
    cnt += 1
    yield cnt
    yield 23333
    return "生成器结束"


ge = generator(5)
print(ge.__next__())
print(ge.__next__())
print(ge.__next__())
'''
输出:
Traceback (most recent call last):
2
  File "D:\Pycharm\Project\Pytest\test.py", line 12, in <module>
23333
    print(ge.__next__())
StopIteration: 生成器结束
'''
send方法

对生成器使用send方法,可以给生成器传入一个值,同时也得到生成器的返元素,实现生成器与外部进行沟通

使用send时发送的信息就相当于是yield的值,那么使用一个变量接收yield的值就可以了。

需要注意第一使用send时只能传None,否则会报错。
原因很简单,经过上面的生成器执行过程分析之后,可以知道第一次执行生成器时,执行到yield就结束了也就是没有给yield接收值的机会,所以传值会报错。
这里建议debug一下理解过程

def generator(n: int):
    cnt = 1
    while cnt < n:
        cnt += 1
        message = yield cnt
        print(message)


ge = generator(5)
print(ge.send(1005))
'''
输出:
Traceback (most recent call last):
  File "D:\Pycharm\Project\Pytest\test.py", line 10, in <module>
    print(ge.send(1005))
TypeError: can't send non-None value to a just-started generator
'''

正确使用:

def generator(n: int):
    cnt = 1
    while cnt < n:
        cnt += 1
        message = yield cnt
        print(message)


ge = generator(5)
print(ge.send(None))
print(ge.send(1005))
print(ge.send(1005))
'''
输出:
2
1005
3
1005
4
'''

也可以:

def generator(n: int):
    cnt = 1
    while cnt < n:
        cnt += 1
        message = yield cnt
        print(message)


ge = generator(5)
print(ge.__next__())
print(ge.send(1005))
print(ge.send(1005))
'''
输出:
2
1005
3
1005
4
'''

总之就是一定要先执行一次yield才能用send发非None信息

猜你喜欢

转载自blog.csdn.net/qq_36102055/article/details/107425854