Skip to content

Latest commit

 

History

History
405 lines (291 loc) · 8.88 KB

111_解析式和生成器.md

File metadata and controls

405 lines (291 loc) · 8.88 KB

列表解析式

语法

  • 列表解析式的语法:[返回值 for 元素 in 可迭代对象 if 条件]
  • 使用中括号[],内部是for循环
  • if条件语句可选
  • 返回一个新的列表

列表解析式是一种语法糖,编译器会优化,不会因为简写而影响效率,反而因优化提高了效率。减少程序员工作量,减少出错。简化了代码,但可读性增强。

例子

  1. 比如要生成一个列表,元素0~9,对每一个元素自增1后求平方返回新列表,下面看不用列表解析式和用列表解析式的代码。
>>> lst = []
>>> for i in range(10):
	i = (i+1)**2
	lst.append(i)

>>> lst
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> lst = [(i+1)**2 for i in range(10)]
>>> lst
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  1. 打印20以内的偶数
>>> lst = []
>>> for i in range(20):
	if i%2 == 0:
		lst.append(i)
>>> print(lst)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>
>>> lst = [i for i in range(20) if i%2==0]
>>> print(lst)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

思考

有这样的赋值语句newlist = [print(i) for i in range(10)],请问newlist的元素打印出来是什么?

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

结果为0
1
2
3
4
5
6
7
8
9
[None, None, None, None, None, None, None, None, None, None]

应该注意if条件后面不能在跟elif

比如获取20以内同时被2、3整除,下面的代码是不行的。

>>> lst = [i for i in range(20) if i%2==0 elif 1%3==0]
SyntaxError: invalid syntax

and可以

>>> lst = [i for i in range(20) if i%2==0 and i%3==0]
>>> lst
[0, 6, 12, 18]

列表解析式进阶

第一种

[expr for item in iterable if cond1 if cond2]

这等价于:

ret = []
for item in iterable:
  if cond1:
      if cond2:
        ret.append(expr)

获取20以内同时被2、3整除

>>> lst = [i for i in range(20) if i%2==0 if i%3==0]
>>> lst
[0, 6, 12, 18]

第二种

[expr for i in iterable1 for j in iterable2 ]

等价于:

ret = []
for i in iterable1:
  for j in iterable2:
    ret.append(expr)

相当于嵌套for循环。

  1. 列表里的元素为字典
>>> lst = [{i:j} for i in range(5) for j in 'abc']
>>> lst
[{0: 'a'}, {0: 'b'}, {0: 'c'}, {1: 'a'}, {1: 'b'}, {1: 'c'}, {2: 'a'}, {2: 'b'}, {2: 'c'}, {3: 'a'}, {3: 'b'}, {3: 'c'}, {4: 'a'}, {4: 'b'}, {4: 'c'}]
  1. 列表的元素为集合
>>> lst = [{i,j} for i in range(5) for j in 'abc']
>>> lst
[{0, 'a'}, {0, 'b'}, {0, 'c'}, {1, 'a'}, {1, 'b'}, {1, 'c'}, {2, 'a'}, {2, 'b'}, {'c', 2}, {3, 'a'}, {'b', 3}, {'c', 3}, {4, 'a'}, {'b', 4}, {'c', 4}]
>>> lst = [{1,0} for i in range(5) for j in 'abc']
>>> lst #不存在set去重的考虑
[{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}]
>>> id(lst[0])
3024216889608
>>> id(lst[1])
3024216887592
  1. 复杂结构
>>> lst = [(i,j) for i in range(6) for j in range(20,25) if i >=3 if i**2 >j]
>>> lst
[(5, 20), (5, 21), (5, 22), (5, 23), (5, 24)]

练习

第一题

有一个列表lst = [1,4,9,16,2,5,10,15],生成一个新列表,要求新列表元素是lst相邻2项的和

lst = [1,4,9,16,2,5,10,15]
result = []
for i in range(len(lst)-1):
    result.append(lst[i]+lst[i+1])
print(result)

[5, 13, 25, 18, 7, 15, 25]

#下面用列表解析式
lst = [1,4,9,16,2,5,10,15]
result = [lst[i]+lst[i+1] for i in range(len(lst)-1)]
print(result)

[5, 13, 25, 18, 7, 15, 25]

第二题

打印九九乘法表

先看下普通写法

for i in range(1,10):
    for j in range(1,i+1):
        print("{}*{}={:>2}".format(j,i,j*i),end= '\n' if j == i else ' ')

用列表解析式

lst = [print("{}*{}={:>2}".format(j,i,j*i),end= '\n' if j == i else ' ') for i in range(10) for j in range(2,i+1)]

生成器表达式Generator expression

语法

  • 生成器的语法为:(返回值 for 元素 in 可迭代对象 if 条件)
  • 它就是将列表解析式的中括号换成小括号就行了
  • 它返回的是一个可迭代的生成器对象
>>> (x for i in range(5))
<generator object <genexpr> at 0x000002C0216572C8>

和列表解析式的区别

  • 生成器表达式是按需计算(或称惰性求值、延迟计算),需要的时候才计算
  • 列表解析式是立即返回值

生成器

  • 生成器是Python中一个特殊的程序,用于控制循环的迭代行为。相对于一般函数用return来一次性返回所有值,生成器使用yield关键字,一次只返回一个值。
  • 是一个可迭代对象
  • 是一个迭代器

这样的设计有很大的好处:在数据处理时,如果函数return出来的是一个非常大的数组,那么会非常占用内存,有时会报MemoryError的错误,而使用yield后一次仅仅返回一个元素值,可以优化内存占用的情况

例子

练习一

利用生成器表达式生成生成器

>>> lst = [1,4,9,16,2,5,10,15]
>>> result = (lst[i]+lst[i+1] for i in range(len(lst)-1))  #生成器表达式为我们生成一个生成器对象出来
>>> type(result)
<class 'generator'>

利用生成器

>>> a = (i*2 for i in range(5))
>>> type(a)
<class 'generator'>
>>> next(a)
0
>>> next(a)
2
>>> for i in a:
	print(i)

4
6
8

>>> next(a)
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module> #走到尽头
    next(a)
StopIteration

>>> for i in a:
	print(i)
				#输出为空
>>>

练习二

>>> it = ( print("{}".format(i+1)) for i in range(2))
>>> first = next(it)
1
>>> second = next(it)
2
>>> val = first + second
Traceback (most recent call last):
  File "<pyshell#28>", line 1, in <module>
    val = first + second
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'
>>> first
>>> second
>>> None+None
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    None+None
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'

列表解析式和生成器表达式的区别

计算方法

生成器表达式延迟计算,列表解析式立即计算

内存占用

  • 单从返回值本身来说,生成器表达式节省内存,列表解析式返回新的列表
  • 生成器没有数据,内存占用极少,它是使用的时候一个个返回数据,如果将这些返回的数据合起来占用的内存和列表解析式也差不多,但是它不需要占用这么多内存
  • 列表解析式构造新的列表需要立即占用内存,不管你是否立即使用这么多数据

计算速度

  • 单看计算时间看,生成器表达式耗时非常短,列表解析式时间长
  • 但是生成器本身并没有返回任何值,只返回了一个生成器对象
  • 列表解析式构造并返回了一个新的列表,所有看起来耗时了

应用

集合解析式

语法

  • {返回值 for 元素 in 可迭代对象 if 条件}
  • 列表解析式的中括号换成大括号就行
  • 立即返回一个集合

用法

  • {(x,x+1) for x in range(10)}
  • {[x] for x in range(10)} #错误的,集合里的元素为可哈希,列表不可哈希
>>> {(x,x+1) for x in range(5)}
{(0, 1), (1, 2), (4, 5), (2, 3), (3, 4)}

字典解析式

语法

  • {x:(x+1) for x in range(5)}
{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
  • {x:[x,x+1] for x in range(5)}
{0: [0, 1], 1: [1, 2], 2: [2, 3], 3: [3, 4], 4: [4, 5]}
  • {(x,):[x,x+1] for x in range(5)}
{(0,): [0, 1], (1,): [1, 2], (2,): [2, 3], (3,): [3, 4], (4,): [4, 5]}
  • {chr(0x41+x):x**2 for i in range(10)}
{'C': 4}
  • {str(x):y for x in range(5) for y in range(4)}
{str(x):y for x in range(5) for y in range(4)}