用迭代工具模拟zip和map

======================================================================

我们已经知道了zip怎样组合可迭代对象,也知道了map怎样映射函数。

>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>> list(zip(S1,S2))
[('a', 'x'), ('b', 'y'), ('c', 'z')] >>> list(zip([-2,-1,0,1,2]))
[(-2,), (-1,), (0,), (1,), (2,)] >>> list(map(abs,(-2,-1,0,1,2)))
[2, 1, 0, 1, 2] >>> list(map(pow,[1,2,3],[2,3,4,5]))
[1, 8, 81]

---------------------------------------------------------------------------------------------------------------

以下就能够编写自己的map(func,...)了

>>> def mymap(func,*seqs):
res = []
for args in zip(*seqs):
res.append(func(*args))
return res >>> print(mymap(abs,(-2,-1,0,1,2)))
[2, 1, 0, 1, 2]
>>> print(mymap(pow,[1,2,3],[2,3,4,5]))
[1, 8, 81]

这个版本号依赖于特殊的*args參数传递语法。它手机多个序列參数,将其作为zip參数解包以便组合,然后成对的zip结果解包作为參数以便传入到函数。

也就是说,我们在使用这种一个事实,zip是map中的一个主要的嵌套操作。

【一定要掌握写这样的函数的高级技巧,对參数的处理!】

然而。实际上,前面的版本号展示了列表解析模式,在一个for循环中构建操作结果的一个列表。所以,这个函数还能够精简:

>>> def mymap(func,*seqs):
return [func(*args) for args in zip(*seqs)] >>> print(mymap(abs,(-2,-1,0,1,2)))
[2, 1, 0, 1, 2]
>>> print(mymap(pow,[1,2,3],[2,3,4,5]))
[1, 8, 81]

当这段代码执行的时候,结果与前面同样,可是。这段代码更加精炼而且可能执行地更快。

只是。这两个版本号都是一次性构建结果列表,对于较大的列表来说,这可能浪费内存。既然已经知道了生成器函数和表达式,又一次编码这两种替代方案来依据需求产生结果是非常easy的:

>>> def mymap(func,*seqs):
for args in zip(*seqs):
yield func(*args) >>> def mymap(func,*seqs):
return (func(*args) for args in zip(*seqs)) >>> print(list(mymap(pow,[1,2,3],[2,3,4,5])))
[1, 8, 81]

当这段代码执行的时候,结果与前面同样。可是,这段代码更加精炼而且可能执行地更快。



只是。这两个版本号都是一次性构建结果列表,对于较大的列表来说,这可能浪费内存。既然已经知道了生成器函数和表达式。又一次编码这两种替代方案来依据需求产生结果是非常easy的:

>>> def mymap(func,*seqs):
for args in zip(*seqs):
yield func(*args) >>> def mymap(func,*seqs):
return (func(*args) for args in zip(*seqs)) >>> print(list(mymap(pow,[1,2,3],[2,3,4,5])))
[1, 8, 81]

生成器的版本号产生相同的结果,可是返回设计用来支持迭代协议的生成器。

第一个版本号每次yield一个结果。第二个版本号返回一个生成器表达式的结果做相同的事情。

---------------------------------------------------------------------------------------------------------------

编写自己的zip(...)

前述的样例中的魔力在于,它们使用zip内置函数来配对来自多个序列的參数。

以下。我们也来模拟内置的zip。

>>> def myzip(*seqs):
seqs = [list(S) for S in seqs]
res = []
while all(seqs):
res.append(tuple(S.pop(0) for S in seqs))
return res >>> S1,S2 = 'abc','xyz123'
>>> print(myzip(S1,S2))
[('a', 'x'), ('b', 'y'), ('c', 'z')]

注意这里的all的内置函数的使用,假设一个可迭代对象中的全部元素为True(或者对等的为非空),它返回True。当列表中有參数在删除后变为了空,这个内置函数用来停止循环。

然而,和前面一样。既然我们的zip构建并返回列表。用yield将它们转换为生成器以便它们每一个都是每次返回结果中的一项,这样来节省内存。

>>> def myzip(*seqs):
seqs = [list(S) for S in seqs]
while all(seqs):
yield tuple(S.pop(0) for S in seqs) >>> list(myzip([1,2,3],('a','b','c','d')))
[(1, 'a'), (2, 'b'), (3, 'c')]

当然,也能够通过计算最小的參数长度来完毕其工作,有了最小长度,非常easy编写编写嵌套的列表解析来遍历參数索引范围

>>> def myzip(*seqs):
minlen = min(len(S) for S in seqs)
return [tuple(S[i] for S in seqs) for i in range(minlen)] >>> myzip([1,2,3],('a','b','c','d'))
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> def myzip(*seqs):
minlen = min(len(S) for S in seqs)
for i in range(minlen):
yield tuple(S[i] for S in seqs) >>> list(myzip([1,2,3],('a','b','c','d')))
[(1, 'a'), (2, 'b'), (3, 'c')]

这里第一个样例返回了一个列表。第二个样例使用了生成器函数。但事实上这里也能够使用生成器表达式,反而更精炼:

>>> def myzip(*seqs):
minlen = min(len(S) for S in seqs)
return (tuple(S[i] for S in seqs) for i in range(minlen)) >>> myzip([1,2,3],('a','b','c','d'))
<generator object <genexpr> at 0x02BF1B98>
>>> list(myzip([1,2,3],('a','b','c','d')))
[(1, 'a'), (2, 'b'), (3, 'c')]

======================================================================

对迭代的各种方法进行计时

列表解析要比for循环语句有速度方面的性能优势,并且map会根据调用方法的不同表现出更好或更差的性能。生成器表达式看起来比列表解析速度更慢一些。可是它们把内存需求降到了最小。

---------------------------------------------------------------------------------------------------------------

对模块计时

Python对代码计时非常easy。要看看迭代选项是怎样叠加起来的,让我们从编写一个模块文件里的简单但通用的计时器工具函数開始,从而使其能够用于各类程序中。

#File mytimer.py

import time
reps = 1000
repslist = range(reps) def timer(func,*pargs,**kargs):
start = time.clock()
for i in repslist:
ret = func(*pargs,**kargs)
elapsed = time.clock() - start
return (elapsed,ret)

这个模块通过获取时间開始、调用函数固定的次数而且用開始时间减去停止时间。从而对不论什么位置和keyword參数调用随意函数进行计时。

注意下面几点:

(1)Python的time模块同意訪问当前时间。精度随着每一个平台而有所不同。

在Windows上,这个调用号称可以达到微妙的精度。已经相当准确了。

(2)range调用放到了计时循环之外,由于。它的构建成本不会计算到Python2.6的计时器函数中。

在Python3.0中的range是一个迭代器。因此这个步骤是不须要的。

(3)reps计数是一个全局变量,假设须要的话,导入者能够改动它:mytimer.reps = N



当这些完毕后,全部调用的总的时间在一个元祖中返回,还带有被计时的函数的终于返回值,以便调用者能够验证其操作。

---------------------------------------------------------------------------------------------------------------

计时脚本

如今,要计时迭代工具的速度,执行例如以下脚本,它使用已经学习过的各种列表构建技术的相对速度的计时器模块。

# File timeseqs.py

import mytimer,sys
reps = 10000
repslist = range(reps) def forLoop():
res = []
for x in repslist:
res.append(abs(x))
return res def listComp():
return [abs(x) for x in repslist] def mapCall():
return list(map(abs,repslist)) def genExpr():
return list(abs(x) for x in repslist) def genFunc():
def gen():
for x in repslist:
yield abs(x)
return list(gen()) print(sys.version)
for test in (forLoop,listComp,mapCall,genExpr,genFunc):
elapsed,result = mytimer.timer(test)
print('-'*33)
print('%-9s:%.5f => [%s...%s]'%(test.__name__,elapsed,result[0],result[-1]))

这段脚本測试了五种构建结果列表的替代方法。而且,每种方法都运行了一千万次级别的步骤。也就是说。五个測试中的每个都构建了拥有10000个元素的列表1000次。

要注意,底部的代码怎样遍历4个函数对象的一个元祖并打印出每个__name__,这是一个内置的属性。

---------------------------------------------------------------------------------------------------------------

计时结果

在我的Window10电脑上測试。输出了例如以下结果——map比列表解析稍微快一点。但二者都比for循环要快非常多,而且生成器表达式和生成函数速度居中。

>>>
3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit (Intel)]
---------------------------------
forLoop :1.20765 => [0...9999]
---------------------------------
listComp :0.77999 => [0...9999]
---------------------------------
mapCall :0.67569 => [0...9999]
---------------------------------
genExpr :0.91294 => [0...9999]
---------------------------------
genFunc :0.87446 => [0...9999]

假设改动这段脚本。在每次迭代上运行一个真正的操作(如加法)。而不是调用abs这种小的内置函数,五个函数改为:

def forLoop():
res = []
for x in repslist:
res.append(x+10)
return res def listComp():
return [x+10 for x in repslist] def mapCall():
return list(map(lambda x:x+10,repslist)) def genExpr():
return list(x+10 for x in repslist) def genFunc():
def gen():
for x in repslist:
yield x+10
return list(gen())

看看測试结果:

>>>
3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit (Intel)]
---------------------------------
forLoop :1.12030 => [10...10009]
---------------------------------
listComp :0.64926 => [10...10009]
---------------------------------
mapCall :1.35093 => [10...10009]
---------------------------------
genExpr :0.79881 => [10...10009]
---------------------------------
genFunc :0.85247 => [10...10009]

这时候,针对map调用的是自己定义的一个lambda函数,所以它比for循环慢。但列表解析依旧是最快的。

解释器优化是内部化的一个问题。像这样对Python代码进行性能分析是一件很须要技术的事情。其实不可能推測哪种方法会运行地更好。并且性能应该不是我们编写Python代码时首要关心的问题——要优化Python代码,首先为了可读性和简单性而编写代码。然后,假设须要的话,再优化。

======================================================================

问题:



1.生成器和迭代器有什么关系?



生成器是支持迭代协议的对象:它们有__next__方法。反复前进到系列结果中的下个元素,以及到系列尾端时引发例外事件。在Python中,我们能够用def、加圆括号的列表解析的生成器表达式以及以类定义特殊方法__iter__来创建生成器对象,通过它们来编写生成器函数。



2.yield语句是做什么的?



当有了yield语句时,这个语句会让Python把函数特定的编译成生成器。当调用时。会返回生成器对象,支持迭代协议。

当yield语句执行时,会把结果返回给调用者,让函数的状态挂起。然后。当调用者再调用__next__方法时。这个函数就能够又一次在上次yield语句后继续执行。生成器也能够有return语句。用来终止生成器。

Python——迭代器和解析(3)的更多相关文章

  1. Python 迭代器和列表解析

    Python 迭代器和列表解析 1)迭代器 一种特殊的数据结构,以对象形式存在 >>> i1 = l1.__iter__() >>> i1 = iter(l1) 可 ...

  2. python中html解析-Beautiful Soup

    1. Beautiful Soup的简介 简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据.官方解释如下: Beautiful Soup提供一些简单的.pyt ...

  3. python 关键字yield解析

    python 关键字yield解析 yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator.y ...

  4. Python 迭代器和生成器(转)

    Python 迭代器和生成器 在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的, ...

  5. 一文搞懂Python迭代器和生成器

    很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...

  6. Python迭代器,可迭代对象,生成器

    迭代器 迭代器(iterator)有时又称游标(cursor)是程式设计的软件设计模式,可在容器物件(container,例如链表或阵列)上遍访的界面,设计人员无需关心容器物件的内存分配的实现细节. ...

  7. python学习(解析python官网会议安排)

    在学习python的过程中,做练习,解析https://www.python.org/events/python-events/ HTML文件,输出Python官网发布的会议时间.名称和地点. 对ht ...

  8. python迭代器与iter()函数实例教程

    python迭代器与iter()函数实例教程 发布时间:2014-07-16编辑:脚本学堂 本文介绍了python迭代器与iter()函数的用法,Python 的迭代无缝地支持序列对象,而且它还允许程 ...

  9. Python迭代器,生成器--精华中的精华

    1. 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大 ...

随机推荐

  1. XML案例(简单的考生成绩管理系统)

    1.以如下格式的exam.xml文件为例 <?xml version="1.0" encoding="UTF-8" standalone="no ...

  2. (Go)10.流程控制示例

    package main import ( "math/rand" "fmt" ) func main() { //var n int n := rand.In ...

  3. lodop多打印一页白纸

    [错误还原]Lodop多张空白页测试2 [错误还原]Lodop多出空白页测试 http://blog.sina.com.cn/s/blog_157ebf1370102wta1.html 上面这个链接是 ...

  4. Scala学习2 ———— 三种方式完成HelloWorld程序

    三种方式完成HelloWorld程序 分别采用在REPL,命令行(scala脚本)和Eclipse下运行hello world. 一.Scala REPL. 按照第一篇在windows下安装好scal ...

  5. MySQL 数据的增删改查

    一.数据库的增删改 一. 在MySQL管理软件中,可以通过SQL语句中的DML语言来实现数据的操作,包括 1.使用INSERT实现数据的插入 2.UPDATE实现数据的更新 3.使用DELETE实现数 ...

  6. Vue 区别

    computed和methods区别 效果是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值. 而methods,在重新渲染的时候,函数总会重新调用执行.

  7. SQL Server对数据进行删除

    SQL Server对数据进行删除,把页面的信息从数据库删除. auto"> <tr style="background:red"> <td> ...

  8. Appium Desired Capabilities信息配置

    编写APPium脚本,必须要配置Desired Capabilities信息 Desired Capabilities 在启动 session 的时候是必须提供的. Desired Capabilit ...

  9. day34-3 类和对象小知识

    目录 属性查找顺序 类与对象的绑定方法 类与数据类型 对象的高度整合 属性查找顺序 属性查找顺序:先从对象自身查找,对象没有就去类中查找,类中没有则报错 class Student: name = ' ...

  10. scrapy-redis使redis不止保存url

    先看scrapy-redis源码 class RedisMixin(object): """Mixin class to implement reading urls f ...