知识点:从迭代器一直撸到yield from
最近在跟一个系列,
难度和篇幅比较合适我这样的懒人。
敲下代码,作下注释,看看输出,就蛮好。
https://www.cnblogs.com/wongbingming/p/9095243.html
import collections
from collections.abc import Iterable, Iterator, Generator
from inspect import getgeneratorstate
"""
借助collections.abc这个模块,
使用isinstance()来类别一个对象是否是可迭代的(Iterable),
是否是迭代器(Iterator),
是否是生成器(Generator)。
"""
astr = "XiaoMing"
print("String: {}".format(astr))
print(isinstance(astr, Iterable))
print(isinstance(astr, Iterator))
print(isinstance(astr, Generator))
# String: XiaoMing
# True
# False
# False
"""
从结果来看,这些可迭代对象都不是迭代器,也不是生成器。
它们有一个共同点,就是它们都可以使用for来循环。
"""
alist = [21, 45, 65, 78]
print("List: {}".format(alist))
print(isinstance(alist, Iterable))
print(isinstance(alist, Iterator))
print(isinstance(alist, Generator))
# List: [21, 45, 65, 78]
# True
# False
# False
adict = {'name': "xiaoming", 'age': 18}
print("Dict: {}".format(adict))
print(isinstance(adict, Iterable))
print(isinstance(adict, Iterator))
print(isinstance(adict, Generator))
# Dict: {'name': 'xiaoming', 'age': 18}
# True
# False
# False
"""
可迭代对象,是其内部实现了,__iter__ 这个魔术方法。
可以通过,dir()方法来查看是否有__iter__来判断一个变量是否是可迭代的。
"""
adeque = collections.deque('abcdefg')
print("deque: {}".format(adeque))
print(isinstance(adeque, Iterable))
print(isinstance(adeque, Iterator))
print(isinstance(adeque, Generator))
# deque: deque(['a', 'b', 'c', 'd', 'e', 'f', 'g'])
# True
# False
# False
"""
对比可迭代对象,迭代器其实就只是多了一个函数而已。
就是__next__(),我们可以不再使用for循环来间断获取元素值。
而可以直接使用next()方法来实现。
可以通过,dir()方法来查看是否有__next__来判断一个变量是否是迭代器的
"""
aIterator = iter(astr)
print("aIterator: {}".format(aIterator))
print(isinstance(aIterator, Iterable))
print(isinstance(aIterator, Iterator))
print(isinstance(aIterator, Generator))
# aIterator: <str_iterator object at 0x00000000023F5358>
# True
# True
# False
"""
而生成器,则是在迭代器的基础上(可以用for循环,可以使用next()),再实现了yield。
yield 是什么东西呢,它相当于我们函数里的return。
在每次next(),或者for遍历的时候,
都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。
正是由于这个机制,才使用生成器在Python编程中大放异彩。
实现节省内存,实现异步编程。
可迭代对象和迭代器,是将所有的值都生成存放在内存中,
而生成器则是需要元素才临时生成,节省时间,节省空间。
"""
aGenerator = (x*x for x in range(10))
print("aGenerator: {}".format(aGenerator))
print(isinstance(aGenerator, Iterable))
print(isinstance(aGenerator, Iterator))
print(isinstance(aGenerator, Generator))
# aGenerator: <generator object <genexpr> at 0x0000000002727FC0>
# True
# True
# True
"""
由于生成器并不是一次生成所有元素,而是一次一次的执行返回,
那么如何刺激生成器执行(或者说激活)呢?
激活主要有两个方法
使用next()
使用generator.send(None)
通过交替执行,说明这两种方法是等价的。
"""
gen = aGenerator
print(gen.send(None))
print(next(gen))
print(gen.send(None))
print(next(gen))
print(gen.send(None))
print(next(gen))
#
#
#
#
#
"""
生成器在其生命周期中,会有如下四个状态
GEN_CREATED # 等待开始执行
GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
GEN_SUSPENDED # 在yield表达式处暂停
GEN_CLOSED # 执行结束
在生成器工作过程中,
若生成器不满足生成元素的条件,
就会/应该 抛出异常(StopIteration)。
我们在自己定义一个生成器的时候,
我们也应该在不满足生成元素条件的时候,抛出异常。
"""
def mygen(n):
now = 0
while now < n:
yield now
now += 1
# 抛出异常
raise StopIteration
gen2 = mygen(2)
print(getgeneratorstate(gen2))
print(next(gen2))
print(getgeneratorstate(gen2))
print(next(gen2))
gen2.close()
print(getgeneratorstate(gen2))
# GEN_CREATED
#
# GEN_SUSPENDED
#
# GEN_CLOSED
"""
协程和线程,有相似点,多个协程之间和线程一样,只会交叉串行执行;
也有不同点,线程之间要频繁进行切换,加锁,解锁,
从复杂度和效率来看,和协程相比,这确是一个痛点。
协程通过使用 yield 暂停生成器,
可以将程序的执行流程交给其他的子程序,
从而实现不同子程序的之间的交替执行。
"""
def jumping_range(N):
index = 0
while index < N:
# yield index 是将index return给外部调用程序。
# jump = yield 可以接收外部程序通过send()发送的信息,并赋值给jump
jump = yield index
if jump is None:
jump = 1
index += jump
# 抛出异常
raise StopIteration
itr = jumping_range(5)
print(next(itr))
print(itr.send(2))
print(itr.send(-1))
print(itr.send(None))
print(next(itr))
print(itr.send(-2))
print(next(itr))
#
#
#
#
#
#
"""
yield from后面加上可迭代对象,
他可以把可迭代对象里的每个元素一个一个的yield出来,
对比yield来说代码更加简洁,结构更加清晰。
"""
def gen3(*args, **kwargs):
for item in args:
yield from item
new_list = gen3(astr, alist, adict, aGenerator)
print(list(new_list))
# ['X', 'i', 'a', 'o', 'M', 'i', 'n', 'g', 21, 45, 65, 78, 'name', 'age', 36, 49, 64, 81]
# 子生成器
def average_gen():
total = 0
count = 0
average = 0
while True:
# 子生成器yield的值,直接返回给调用方。
new_num = yield average
if new_num is None:
break
count += 1
total += new_num
average = total/count
# 每一次return,都意味着当前协程结束。
return total, count, average
# 委托生成器
# 作用是:在调用方与子生成器之间建立一个双向通道。
def proxy_gen():
while True:
# 只有子生成器要结束(return)了,
# yield from左边的变量才会被赋值,后面的代码才会执行。
total, count, average = yield from average_gen()
print("计算完毕!!\n总共传入 {} 个数值, 总和:{},平均数:{}"
.format(count, total, average))
# 调用方
def main_gen():
calc_average = proxy_gen()
next(calc_average)
# 调用方可以通过send()直接发送消息给子生成器
print(calc_average.send(10))
print(calc_average.send(20))
print(calc_average.send(30))
calc_average.send(None) # 结束协程
# 如果此处再调用calc_average.send(10),由于上一协程已经结束,将重开一协程
main_gen()
# 10.0
# 15.0
# 20.0
# 计算完毕!!
# 总共传入 3 个数值, 总和:60,平均数:20.0
知识点:从迭代器一直撸到yield from的更多相关文章
- 【Python】迭代器、生成器、yield单线程异步并发实现详解
转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从 ...
- Python:容器、迭代对象、迭代器、生成器及yield关键字
在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list, ...
- python迭代器与生成器及yield
一.迭代器(itertor) 1.可迭代: 在Python中如果一个对象有__iter__()方法或__getitem__()方法,则称这个对象是可迭代的(iterable). 其中__iter__( ...
- python迭代器、生成器、yield理解
简介 yield关键字是python的一种高阶用法,使用yield的函数会返回一个生成器对象,生成器又是一个迭代器,与迭代器相类似的则是可迭代对象,下面首先介绍一下迭代器吧. 迭代器 在python中 ...
- Python知识点进阶——迭代器
可迭代对象 可迭代对象可以简单的理解为用for循环遍历的,如list.tuple.dict.set.str 判断一个对象是否是迭代器: 可以将数据类型 和 是否为可迭代对象 比较来判断是否是可以迭代 ...
- python迭代器、生成器、yield和xrange
https://blog.csdn.net/u010138758/article/details/56291013
- yield 关键字和迭代器
一般使用方法 yield 关键字向编译器指示它所在的方法是迭代器块 在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值. 这是一个返回值,例如,在 forea ...
- 初次使用C#中的yield
这几天在Python程序员的微信订阅号中总是见到yield的关键字,才想起来在C#中也是有yield,但是只是知道有,从来没有了解过他的用法,今天有时间就来看看是怎么使用的.刚开始肯定就是搜索一下用法 ...
- Python迭代器,生成器--精华中的精华
1. 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大 ...
随机推荐
- docker部署安装
docker采用Linux内核技术,所以只能运行在Linux上,所谓的windows平台是使用boot2Docker工具,boot2Docker是在VisualBox构建一个linux精简化环境. B ...
- DOM-Element对象
一. 整体介绍 这里介绍DOM对象中Element对象. 那么何为Element对象呢?Element对象就是HTML元素,Element对象包括:元素节点.文本节点.属性节点. 下面利用一张图来总 ...
- datagrid点删除,弹出一个确认和取消的消息框
有个简单的方法:在datagrid的删除按扭datagrid的属性生成器->列->添加按扭列->删除在数据字段中加上:<div id="de" onclic ...
- JavaScript之DOM概念
一.DOM概念 1.DOM是什么? 1.1 起源.DOM起源于Netscape与Microsoft 公司的DHTML(动态HTML). 1.2 名词解释.Document Object Model,文 ...
- CF418D Big Problems for Organizers
传送门 题意,给一棵树,每次给两个点\(x,y\),求\(\max_{i=1}^{n}(\min(di_{x,i},di_{y,i}))\) 看std看了好久 以下是一个优秀的在线做法,\(O(nlo ...
- Database学习 - mysql 数据库 索引
索引 索引在mysql 中也叫 '键',是存储引擎用来快速找到记录的一种数据结构.索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要. 索引优化应该是对查询性能优化 ...
- javascript方法--bind()
bind方法,顾名思义,就是绑定的意思,到底是怎么绑定然后怎么用呢,下面就来说说我对这个方法的理解. 语法 fun.bind(this,arg1,arg2,...) bind()方法会创建一个新的函数 ...
- 【学习博客】Python学习初体验
本周是正式开始学习Python的第一周.很久不写代码了,毕业第5年了,期间几乎没怎么动过手.这段时间比较规律.密集的学习又让我找回了当时的感觉,还不算陌生,我挺喜欢的这种能实实在在看到自己知识增长的状 ...
- VMware12虚拟机中Ubuntu16.04安装CPU版本Caffe
首先,可以自行下载VMware12进行安装,基本上都是直接点击‘下一步’直到安装完成,这里重点讲一下Ubuntu16及Caffe的安装步骤 第一步: 下载Ubuntu16.04版本的文件,这里给出链接 ...
- Freemarker导出word的简单使用
1.模板 username:${username} password:${password} <#list mylist as item> ${item.name!} ${item.pas ...