Python——迭代器和解析(3)

用迭代工具模拟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语句。用来终止生成器。

时间: 2024-08-28 06:24:02

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

Python之 迭代器 列表解析器

回顾: 1.动态语言 sys.getrefcount()  对象被引用的次数. 增加对象引用次数的场景:以赋值方式创建对象:对象作为某容器的元素时:被当做参数传递给函数时:为对象创建另外的变量名: 减少引用计数:引用此对象的某变量名被显式销毁 del x :给引用此对象的变量名重新赋值:从容器中移除对象,list.pop():容器本身被销毁: 完备和非完备遍历 Python迭代器 又称游标,是程序设计的软件设计模式,是一种可在容器物件上实现元素遍历的接口.是一种特殊的数据结构,在Python中,

python迭代器、生成器和yield语句

http://blog.csdn.net/pipisorry/article/details/22107553 一.迭代器(iterator) 迭代器:是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration.任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开. 迭代器对象要求支持迭代

Python迭代器和生成器介绍

Python迭代器和生成器介绍 迭代器 迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration. 在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作. 常用的几个内建数据结构tuple.list.set.dict都支持迭代器,字符串也可以使用迭代操作. 你也可以自己实现一个迭代器,如上所述,只

Lesson 023 —— python 迭代器与生成器

Lesson 023 -- python 迭代器与生成器 迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 迭代器有两个基本的方法:iter() 和 next(). 字符串,列表或元组对象都可用于创建迭代器: >>>list=[1,2,3,4] >>> it = iter(list) # 创建迭代器对象 >>

[Python]BeautifulSoup—HTML解析包

今天真机调试的时候莫名其妙遇到了这样的一个问题: This product type must be built using a provisioning profile, however no provisioning profile matching both the identity "iPhone Developer" and the bundle identifier..... 具体如下图所示: 十分蛋疼, 发现不管是从网上下的demo, 还是自己的过程.凡事真机测试的时候都

Python中用ElementTree解析XML

[XML基本概念介绍] XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据. 概念一: <foo> # foo元素的起始标签 </foo> # foo元素的结束标签 # note: 每一个起始标签必须有对应的结束标签来闭合, 也可以写成<foo/> 概念二: <foo> # 元素可以嵌套到任意参次 <bar></bar> # bar元素为foo元素的子元素 </f

利用Python迭代器查找最小值和最大值

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration) #利用Python迭代器查找最小值和最大值def findMaxandMin(l): if l==[]: return (none,none) else: min=l[0] max=l[0] for i in l: if max<i: max=i if min>i: min=i return(max,min) print(findMaxandMin([2,4,1,

Python的各种解析操作,和数学概念中的解析有何联系?

python中的解析 Python支持各种解析(comprehension)操作,比如列表解析.集合解析.元组解析.字典解析.它们根据某些元素来创建(推导)出一个新的列表.集合.元组.字典等.所以有的地方也称为推导,比如列表推导.集合推导等. 下面是一个列表解析的示例: 1 >>> [ i*2 for i in range(10) if i % 2 == 0 ] 2 [0, 4, 8, 12, 16] 这里是列表解析,因为使用的中括号[ xxxx ],它表示根据条件推导出一个新的列表.P

python threading实例解析diaoyo&#39;n

1.认识GIL: 说到GIL一直是代码专家们一直以来想要解决的问题,也是被许多程序员诟病的,下面带领大家看下官方threading模块document中如何去描述对于GIL这个全局解释器锁的:https://docs.python.org/3/library/threading.html 全局解释器锁 所使用的机制的CPython解释器来确保只有一个线程执行的Python 字节码在一个时间.通过使对象模型(包括关键的内置类型,例如dict)隐式安全地防止并发访问,从而简化了CPython的实现.