python学习--关注容易被忽略的知识点--(三)高级特性

本文深入解析Python中的迭代器与生成器概念,涵盖可迭代对象与迭代器的区别,列表生成式与生成器表达式的应用,以及如何利用yield关键字创建生成器。适合有一定Python基础的学习者进阶学习。

本系列文章回顾了 python大部分关键的知识点,关注那些容易被忽略的知识点。适用于有一定python基础的python学习者。
本系列文章主要参考廖雪峰的python学习网站。该学习网站内容全面,通俗易懂,强烈推荐初学者在这个网站学习python。

全系列
(一)python基础
(二)函数
(三)高级特性
(四)函数式编程
(五)面向对象编程

高级特性

迭代

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。如:

>>> for ch in 'ABC':
...     print(ch)
...
A
B
C

如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False

列表生成式

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 双层循环
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

列表生成式+if…else
不能在for循环后面加else,在for循环前面加if必须加上else

>>> [x if x % 2 == 0 for x in range(1, 11)]
  File "<stdin>", line 1
    [x if x % 2 == 0 for x in range(1, 11)]
                       ^
SyntaxError: invalid syntax

>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

在一个列表生成式中,for前面的if … else是表达式,而for后面的if是过滤条件,不能带else。

生成器

列表生成式的缺点是会受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,会占用很大的存储空间,存储效率大大降低。
在Python中,生成器(generator)是一种一边循环一边计算的机制,可以在循环的过程中不断推算出后续的元素,因此不必创建完整的list,从而节省大量空间。

创建生成器的方法

  • 将列表生成式的[]改为()
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

#如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
...
# 计算到最后一个元素,再next会报错
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

# 正确的方法是for循环
>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
  • 函数实现
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。举例:

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)

>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

>>> g = fib(6)
>>> while True:
...     try:
...         x = next(g)
...         print('g:', x)
...     except StopIteration as e:
...         print('Generator return value:', e.value)
...         break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

迭代器

可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象。

>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象。

总结:

  • 凡是可作用于for循环的对象都是Iterable类型;
  • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
    更多关于迭代对象和迭代器的知识见:https://www.jianshu.com/p/d917b37030ef
    https://www.jb51.net/article/190712.htm

补充

可迭代对象和迭代器的本质:

在这里插入图片描述

  • 可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.
  • 一个具备了__iter__方法的对象,就是一个可迭代对象。
  • list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。iter()函数实际上就是调用了可迭代对象的__iter__方法。
  • 迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。
  • python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可。
  • 一个实现了__iter__方法和next()方法的对象,就是迭代器。
  • 除了Python内置的iter之外,还可以通过Python内置的工具包itertools创建迭代器。

for…in…循环本质

for item in Iterable循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

生成器和yield

  • 生成器一定是迭代器,但是迭代器不全是生成器对象。
  • 生成器的的特点是运行速度块,最大优点是节省内存。
  • 包含yield关键字的函数将是一个生成器对象。
  • 这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。
  • 除了yield之外,在Python3.3之后还加入了yield from获取生成器,允许一个生成器将其部分操作委派给另一个生成器,yield from后面需要加上可迭代对象,这样可以把可迭代对象变成生成器。
  • python3的range()就是xrange(),便是一个生成器。
  • yield的另外用法包括协程、定义上下文管理器。

参考链接:

Python 的关键字 yield 有哪些用法和用途?
python中yield的用法详解——最简单,最清晰的解释

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lingpy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值