python之推导式and生成器表达器笔记及面试坑

推导式是从一个或多个迭代器快速简洁的创建数据结构的一种办法,它可以将循环和条件判断结合,从而可以避免语法冗长的代码。

推导式:一行代码生成一个列表(字典、集合)

注意:元组没有推导式

基本语法:[结果 for循环 条件判断(可选)]

列表习题

例1:创建一个1到10的列表

解答:

(1)循环

1
2
3
4
lis = []
for i in range(1, 11):
lis.append(i)
print(lis)

(2)列表推导式

1
2
lis = [i for i in range(1, 11)]
print(lis)

例2:20以内的基数创建一个列表

解答:

  • 取余判断

    1
    lis = [ i for i in range(21) if i%2==1]
  • range步长

    1
    lis = [i for i in range(1,20,2)]

例3:100以内被3整除的数的平方组成一个新的列表

  1. 使用range步长实现

    1
    lis = [i**2 for i in range(3, 100, 3)]
  2. 使用判断表达式实现

    1
    lis = [ i**2 for i in range(1, 100) if i%3==0]

例4:列表中包含两个e的名字组成一个新的列表返回

  1. 使用for循环实现

    1
    2
    3
    4
    5
    6
    names = [['tom', 'billy', 'jeffer', 'andrew', 'wesley', 'steven'], ['alice', 'jill', 'ada', 'eveal', 'eva', 'cherry']]
    lis = []
    for line in names:
    for name in line:
    if name.count('e') == 2:
    lis.append(name)
  2. 使用列表推导式实现

    1
    2
    names = [['tom', 'billy', 'jeffer', 'andrew', 'wesley', 'steven'], ['alice', 'jill', 'ada', 'eveal', 'eva', 'cherry']]
    lis = [name for line in names for name in line if name.count('e') == 2]

字典推导式习题

1
语法:{key:value for循环 条件判断(可选)}

例1:使用字典推导式将[‘a’, ‘b’, ‘c’, ‘d’] 转换为 {0:’a’, 1:’b’, 2:’c’, 3:’d’}

  1. 推导式使用range

    1
    2
    lis = ['a', 'b', 'c', 'd']
    dic = {i:lis[i] for i in range(len(lis))}
  2. 推导式使用enumerate

    1
    2
    lis = ['a', 'b', 'c', 'd']
    dic = {i:j for i, j in enumerate(lis)}

例2:将{‘jj’:’林俊杰’, ‘jay’:’周杰伦’, ‘jolin’:’蔡依林’}键与值对调形成新的字典

1
dic = {'jj':'林俊杰', 'jay':'周杰伦', 'jolin':'蔡依林'} res = {v:k for k,v in dic.items()} print(res)

集合推导式习题

1
集合:不可重复、可哈希

例:将以下列表去重[1, 3, 5, 3, 66, 4, 3, 5, 7]

  1. 使用集合转换去重

    1
    2
    lis = [1, 3, 5, 3, 66, 4, 3, 5, 7]
    s = set(lis)
  2. 使用推导式去重

    1
    2
    lis = [1, 3, 5, 3, 66, 4, 3, 5, 7]
    s = {i for i in lis}

生成器表达式和列表推导式的区别:

  1. 列表推导式比较耗内存. 一次性加载. 生成器表达式几乎不占用内存. 使用的时候才分 配和使用内存

  2. 得到的值不一样. 列列表推导式得到的是一个列表. 生成器表达式获取的是一个生成器.


生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)

生成器表达式可以直接获取到生成器对象. 生成器对象可以直接进行for循环. 生成器具有惰性机制.

生成器的惰性机制: 生成器只有在访问的时候才取值. 说白了. 你找他要他才给你值. 不找他要. 他是不会执行的.

下面放两个带坑面试题

1、以下代码运行结果是什么?(先不要看解释哟)

1
2
3
4
5
6
7
8
9
def func():
yield 222
g = func() # 生成器g
g1 = (i for i in g) # 生成器g1. 但是g1的数据来源于g
g2 = (i for i in g1) # 生成器g2. 来源g1
print(list(g)) # 获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕.
print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了. g1 也就没有数据

print(list(g2)) # 和g1同理

答案:

1
2
3
[222]
[]
[]

2、以下代码运行结果是什么?

1
2
3
4
5
6
7
8
9
def add(a, b):
return a + b
def test():
for item in range(4):
yield item
g = test()
for n in [5, 10]:
g = (add(n, i) for i in g)
print(list(g))

答案:[20, 21, 22, 23]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
以上代码分析:
首行add函数只是一个用来求和的迷惑函数
向下看
test()函数是一个生成器函数,可以看到它的值循坏下来为:0, 1, 2, 3
g = test()这行代码才开始获取生成器,然后继续向下看
for n in [5, 10]:
g = (add(n, i) for i in g)
这段代码我们来进行拆分一下变为:

n=5
g = (add(n, i) for i in g)

n=10
g = (add(n, i) for i in g)

print(list(g))
以上代码可以看出,n=5以后,我们并没有取生成器的值,所以5不会带进生成器表达式,只是执行了下一行代码g = (add(n, i) for i in g),
所以此时我们可以将代码再进行拼接下将n=10后的生成器表达式后面的g给替换为(add(n, i) for i in g)
n=10
g = (add(n, i) for i in (add(n, i) for i in g))

print(list(g))
此时我们才取值
所以可以把n等于10带进去
代码变为一下,为了区分循环变量i,我们将替换g表达式中的i改为index
g = (add(10, i) for i in (add(10, index) for index in g))
接下来我们一步一步开始走
g = (add(10, i) for i in (add(10, index) for index in [0, 1, 2, 3]))
继续
g = (add(10, i) for i in [10, 11, 12, 13])
继续g的值已经是20,21, 22, 23
print(list(g))
最后转为列表,结果当然就是[20, 21, 22, 23]