Python 之 列表/集合/字典解析式、生成器表达式、迭代器和可迭代对象 的深入浅出

Python 之 列表/集合/字典解析式、生成器表达式、迭代器和可迭代对象 的深入浅出

  • 1、列表解析式 List Comprehension
    • 1.1 语法
    • 1.2 优势
    • 1.3 多条件 列表解析式
    • 1.4 多元素 列表解析式
    • 1.4 示例
      • 1.4.1 获取 10 以内的偶数
      • 1.4.2 以下代码生成的列表是什么
      • 1.4.3 20以内,既能被 2 整除又能被 3 整除的数
      • 1.4.4 思考以下 列表解析式 生成的列表
      • 1.4.4 列表解析式 里的列表元素
  • 2、生成器表达式 Generator Expression
    • 2.1 语法
    • 2.2 生成器表达式 本质
    • 2.3 生成器特点
    • 2.4 生成器表达式 优势
    • 2.5 示例
      • 2.5.1 从 示例 来看 列表解析式和生成器表达式 区别
        • 2.5.1.1 生成器表达式
        • 2.5.1.2 列表解析式
  • 3、集合解析式
    • 3.1 语法
    • 3.2 示例
  • 4、集合解析式
    • 4.1 语法
    • 4.2 示例
  • 5、迭代器
    • 5.1 概念
    • 5.2 示例
  • 6、可迭代对象
    • 6.1 概念
    • 6.2 示例
  • 7、总结

1、列表解析式 List Comprehension

1.1 语法

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

1.2 优势

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

1.3 多条件 列表解析式

  • [expr for item in iterable if cond1 if cond2 if cond3 ...]

  • 上面代码等价于下面代码:

    # 适合于 一斜到底 单分支 if 语句
    ret = []
    for item in iterable:
    	if cond1:
    		if cond2:
    			if cond3:
    				ret.append(expr)
    

1.4 多元素 列表解析式

  • [expr for i in iterable1 for j in iterable2]

  • 上面代码等价于下面代码:

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

1.4 示例

1.4.1 获取 10 以内的偶数

# 方法一
even = []

for i in range(10):
    if i % 2 == 0:
        even.append(i)

print(even)
[0, 2, 4, 6, 8]
# 方法二
even = [x for x in range(10) if x % 2 == 0]

print(even)
[0, 2, 4, 6, 8]
  • 两种方法的效率比较
# Jupyter Notebook 环境
%%timeit
even = [] 

for i in range(100):
    if i % 2 == 0:
        even.append(i)
6.03 µs ± 26.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# Jupyter Notebook 环境
%timeit ([i for i in range(100) if i % 2 == 0])
4.57 µs ± 26 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

1.4.2 以下代码生成的列表是什么

  • print() 函数的返回值为 None,所以生成的列表是 5 个 None
newlist = [print(i) for i in range(5)]

print('newlist = {}'.format(newlist))
0
1
2
3
4
newlist = [None, None, None, None, None]

1.4.3 20以内,既能被 2 整除又能被 3 整除的数

[i for i in range(20) if i % 2 == 0 and i % 3 ==0]

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

# [0, 6, 12, 18]

1.4.4 思考以下 列表解析式 生成的列表

[(x, y) for x in 'abc' for y in range(2)]

# [('a', 0), ('a', 1), ('b', 0), ('b', 1), ('c', 0), ('c', 1)]
[[x, y] for x in 'abc' for y in range(2)]

# [['a', 0], ['a', 1], ['b', 0], ['b', 1], ['c', 0], ['c', 1]]
[{
     x, y} for x in 'abc' for y in range(2)]

# [{0, 'a'}, {1, 'a'}, {0, 'b'}, {1, 'b'}, {0, 'c'}, {1, 'c'}]
[(i, j) for i in range(7) if i > 4 for j in range(20, 25) if j > 22]

[(i, j) for i in range(7) for j in range(20, 25) if i > 4 if j > 22]

[(i, j) for i in range(7) for j in range(20, 25) if i > 4 and j > 22]

# [(5, 23), (5, 24), (6, 23), (6, 24)]

1.4.4 列表解析式 里的列表元素

  • 从下例可以看出,列表解析式里的列表元素 id 不一样,所以不是同一个引用列表
list1 = [[1, 0] for x in 'abce' for y in range(2)]

print(list1)

for i in range(len(list1)):
    print(id(list1[i]))
[[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
117234952
117235016
117235272
117235208
117235144
117235080
117235400
117235336

2、生成器表达式 Generator Expression

2.1 语法

  • ( 返回值 for 元素 in 可迭代对象 if 条件 )
  • 列表解析式中的方括号换为小括号就行了
  • 返回一个生成器

2.2 生成器表达式 本质

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

2.3 生成器特点

  • 可迭代对象
  • 迭代器

2.4 生成器表达式 优势

  • 计算方式:生成器表达式延迟计算,列表解析式立即计算
  • 内存占用:
    1、但从返回值本身来说,生成器表达式省内存,列表解析式返回新的列表
    2、生成器没有数据,占用内存极少,它是使用时一个个返回数据。如果将这些返回的数据合起来占用的内存和列表解析式差不多。但是,它不需要立即占用那么多内存
    3、列表解析式构造新的列表需要立即占用内存,不管你是否李捷使用这么多数据
  • 计算速度
    1、单看计算时间,生成器表达式耗时非常短,列表解析式耗时长
    2、但是生成器本身并没有返回任何值,只返回了一个生成器对象
    3、列表解析式构造并返回了一个新的列表,所以耗时较长

2.5 示例

2.5.1 从 示例 来看 列表解析式和生成器表达式 区别

2.5.1.1 生成器表达式

从下面示例中可以总结出:

  • 生成器表达式 是 延迟计算,可以使用 next(g)获取
  • 生成器表达式 返回 一个迭代器,可以迭代
  • 从前到后走完一遍后,不能回头
  • 使用 for 可以遍历 生成器
  • 如果 生成器 已迭代完毕,使用 next() 会报错,使用 for 不会
g = ('{:04}'.format(i) for i in range(5))
print(1, 'g is {}.'.format(g))
print(2, 'type(g) is {}.'.format(type(g)))

print(3, next(g))
for x in g:
    print(x)

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

for x in g:
    print(x), type(x)
    
print('5============5')

print(6, next(g))
1 g is <generator object <genexpr> at 0x00000000077D9548>.
2 type(g) is <class 'generator'>.
3 0000
0001
0002
0003
0004
4============4
5============5
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-65-8121dfd1b116> in <module>
     13 print('5============5')
     14 
---> 15 print(6, next(g))

StopIteration: 

2.5.1.2 列表解析式

从下面示例中可以总结出:

  • 列表解析式 是 立即计算
  • 列表解析式 返回的不是迭代器,返回可迭代对象列表
  • 从前到后走完一遍后,可以再次回头迭代
g = ['{:04}'.format(i) for i in range(5)]
print(1, 'g is {}.'.format(g))
print(2, 'type(g) is {}.'.format(type(g)))

for x in g:
    print(x)

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

for x in g:
    print(x), type(x)
    
print('4============4')
1 g is ['0000', '0001', '0002', '0003', '0004'].
2 type(g) is <class 'list'>.
0000
0001
0002
0003
0004
3============3
0000
0001
0002
0003
0004
4============4

3、集合解析式

3.1 语法

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

3.2 示例

set1 = {
     (x, x+1) for x in range(5)}
print(set1, type(set1))

# {(0, 1), (1, 2), (4, 5), (2, 3), (3, 4)} 
set2 = {
     (i, j) for i in range(2) for j in range(2)}
print(set2, type(set2))

# {(0, 1), (1, 0), (0, 0), (1, 1)} 
{
     [x] for x in range(5)}

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-69-a39ffa943c7e> in <module>
----> 1 {
     [x] for x in range(5)}

<ipython-input-69-a39ffa943c7e> in <setcomp>(.0)
----> 1 {
     [x] for x in range(5)}

TypeError: unhashable type: 'list'

4、集合解析式

4.1 语法

  • { 返回值 for 元素 in 可迭代对象 if 条件 }
  • 列表解析式中的中括号换为大括号即可
  • 使用key:value形式
  • 立即返回一个字典

4.2 示例

{
     x:(x, x+1) for x in range(3)}

# {0: (0, 1), 1: (1, 2), 2: (2, 3)}
{
     x:[x, x+1] for x in range(3)}

# {0: [0, 1], 1: [1, 2], 2: [2, 3]}
{
     (x, ):[x, x+1] for x in range(3)}

# {(0,): [0, 1], (1,): [1, 2], (2,): [2, 3]}
{
     chr(0x41+x):x**2 for x in range(3)}

# {'A': 0, 'B': 1, 'C': 4}
{
     str(x):y for x in range(3) for y in range(4)}

# {'0': 3, '1': 3, '2': 3}
# 字典赋值会进行覆盖

5、迭代器

5.1 概念

  • 特殊的对象,一定是可迭代对象,具备可迭代对象的特征

  • 通过 iter 方法把一个可迭代对象封装成迭代器

    Docstring:
    iter(iterable) -> iterator
    iter(callable, sentinel) -> iterator
    
    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.
    Type:      builtin_function_or_method
    
  • 通过 for 方法,迭代 迭代器对象

  • 通过 next 方法,迭代 迭代器对象

  • 生成器对象,也是 迭代器对象

  • 从前到后走完一遍后,不能回头

  • 如果 迭代器 已迭代完毕,使用 next() 会报错,使用 for 不会

5.2 示例

iter1 = iter(range(3))
print(type(iter1))
print(iter1)
for i in iter1:
    print(i)
print('==============')
for i in iter1:
    print(i)
print('==============')
<class 'range_iterator'>
<range_iterator object at 0x0000000008328830>
0
1
2
==============
==============
iter2 = iter(list(range(3)))
print(type(iter2))
print(iter2)
print(1, next(iter2))
for i in iter2:
    print(i)
print('==============')
print(next(iter2))
print('==============')
<class 'list_iterator'>
<list_iterator object at 0x00000000027CA308>
1 0
1
2
==============
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-107-d48de9f46b81> in <module>
      6     print(i)
      7 print('==============')
----> 8 print(next(iter2))
      9 print('==============')

StopIteration:

6、可迭代对象

6.1 概念

  • 能够通过迭代一次次返回 不同元素 的对象
    1、所谓相同,不是指值是否相同,而是元素在容器中是否是同一个
    2、列表中有重复的值,但是索引不同,这就是两个不同的元素
  • 可以迭代,但是未必有序,未必可索引
  • 可迭代对象有:list / tuple / string / bytes / bytearray / range / set / dict / 生成器 / 迭代器
  • 可以使用成员操作符 in / not in
  • in 本质上对于线性结构就是在遍历对象,非线性结构求 hash

6.2 示例

3 in range(5)  # True
3 in (x for x in range(5))  # True
3 in {
     x:y for x,y in zip(range(4), range(4, 10))}  # True

7、总结

  • 一般来说,应该多应用解析式,简短高效
  • 如果一个解析式非常复杂,难以读懂,可以拆解为 for 循环
  • 生成器 和 迭代器 是不同的对象,但都是可迭代对象
  • 可迭代对象范围更大,但都可以使用 for 循环遍历
  • 可迭代对象 > 迭代器对象 > 生成器对象

你可能感兴趣的:(Python,python)