上节提出了range和xrange的效率问题,这节我们来探究其中的原因

 

yield的使用

 

我们看下面的程序:

#coding: utf-8

def test():
print 4
print 2
print 5 if __name__ == '__main__':
test()

这段代码的运行结果当然是没有任何疑问的。

但是如果我将代码修改一下:

#coding: utf-8

def test():
yield 4
yield 2
yield 5 if __name__ == '__main__':
print test()

运行结果有些奇怪:

<generator object test at 0xb71f1144>

我们尝试这样使用:

if __name__ == '__main__':
for i in test():
print i

结果却出人意料:

wing@ubuntu:~/Documents/py|⇒  python 17.py
4
2
5

这是什么原因呢?这里看起来,test()好像一个集合,里面存储了4,2,5,所以我们才能够依次遍历。

实际上,原因并非如此。

当一个函数中含有yield时,这个函数就不再是一个普通的函数,而是一个可迭代的对象(实际上叫做生成器,不过现在不必关心概念)。

同样,执行该函数时,不再是马上执行其中的语句,而是生成一个可迭代对象。当执行迭代的时候,才真正运行其中的代码。

当函数体执行到yield时,便退出这个函数,此时yield具有return的功能。但是这里的关键是,当下次执行这个函数时,并不是从头开始执行,而是从上次yield退出的位置继续执行

尝试下面的代码:

#coding: utf-8

def test():
yield 4
yield 2
yield 5 if __name__ == '__main__':
t = test()
it = iter(t)
print it.next()
print it.next()
print it.next()
print it.next()

运行结果为:

wing@ubuntu:~/Documents/py|⇒  python 17.py
4
2
5
Traceback (most recent call last):
File "17.py", line 14, in <module>
print it.next()
StopIteration

从这里的结果可以看出,test()语句没有执行代码段,而是生成了一个可以迭代的对象。

我们甚至可以得出结论,每当执行一次next,就向后执行到下一个yield语句,或者所有的语句执行完毕。

 

range的实现

 

我们尝试实现range:

#coding: utf-8

def _range(value):
i = 0
result = []
while i < value:
result.append(i)
i += 1
return result if __name__ == '__main__':
for i in _range(4):
print i

range的逻辑比较简单,就是生成一个列表。

 

xrange的模拟实现

 

我们根据前面的结论,猜测xrange是一个含有yield的函数,于是:

#coding: utf-8

def _xrange(value):
i = 0
while i < value:
yield i
i += 1 if __name__ == '__main__':
for i in _xrange(4):
print i

运行一下,结果和我们预期一致。

当然,实际的xrange比我们这里编写的更加复杂,但是基本原理是一致的。

 

为何xrange比range高效?

 

答案很明显了,range是一次性生成所有的数据,而xrange,内部使用了yield关键字,每次只运行其中一部分,这样从头到尾都没有占用大量的内存和时间。所以效率较高。

 

我们再次比较性能,这次比较的是我们自己编写的版本:

#coding: utf-8
import sys
from time import time def _range(value):
i = 0
result = []
while i < value:
result.append(i)
i += 1
return result def _xrange(value):
i = 0
while i < value:
yield i
i += 1 def count_time(func):
def wrapped(*args, **kargs):
begin_time = time()
result = func(*args, **kargs)
end_time = time()
cost_time = end_time - begin_time
print '%s called cost time : %s ms' %(func.__name__, float(cost_time)*1000)
return result
return wrapped @count_time
def test1(length):
for i in _range(length):
pass @count_time
def test2(length):
for i in _xrange(length):
pass if __name__ == '__main__':
length = int(sys.argv[1])
test1(length)
test2(length)

运行结果为:

wing@ubuntu:~/Documents/py|⇒  python 19.py 1000
test1 called cost time : 0.116109848022 ms
test2 called cost time : 0.0619888305664 ms
wing@ubuntu:~/Documents/py|⇒ python 19.py 10000
test1 called cost time : 2.39086151123 ms
test2 called cost time : 0.566959381104 ms
wing@ubuntu:~/Documents/py|⇒ python 19.py 100000
test1 called cost time : 15.5799388885 ms
test2 called cost time : 6.41298294067 ms
wing@ubuntu:~/Documents/py|⇒ python 19.py 1000000
test1 called cost time : 130.295038223 ms
test2 called cost time : 65.4468536377 ms
wing@ubuntu:~/Documents/py|⇒ python 19.py 10000000
test1 called cost time : 13238.3038998 ms
test2 called cost time : 652.212142944 ms

显然,使用yield的版本更加高效。

 

下文,我们探究生成器。

从range和xrange的性能对比到yield关键字(中)的更多相关文章

  1. 从range和xrange的性能对比到yield关键字(上)

    使用xrange   当我们获取某个数量的循环时,我们惯用的手法是for循环和range函数,例如: for i in range(10): print i 这里range(10)生成了一个长度为10 ...

  2. WPF DataGrid与ListView性能对比与场景选择

    开门见山的说 性能对比: 在Demo中,DataGrid与ListView默认开启虚拟化(可以理解为动态渲染,类似懒加载只渲染屏幕可以看见的地方) DataGrid渲染10列50行随机字符280ms ...

  3. [Python]range与xrange用法对比

    [整理内容]具体如下: 先来看如下示例:>>>x=xrange(0,8)>>> print xxrange(8)>>>print x[0]0> ...

  4. 实验比较python中的range和xrange

    1 结论: 全用xrange,除非你需要使用返回的列表 2 实验一:性能对比 实验环境:win7 ,64位系统 python2.7 import time StartTime=time.time() ...

  5. Python从题目中学习:range()和xrange()

    近期给公司培训Python,好好啃了啃书本,查了查资料,总结一些知识点. --------------------------------------------------------------- ...

  6. python 中range与xrange的区别

    先来看看range与xrange的用法介绍 help(range)Help on built-in function range in module __builtin__: range(...) r ...

  7. range和xrange的区别详解

    两种用法介绍如下:1.range([start], stop[, step])返回等差数列.构建等差数列,起点是start,终点是stop,但不包含stop,公差是step.start和step是可选 ...

  8. Suspend to RAM和Suspend to Idle分析,以及在HiKey上性能对比【转】

    转自:https://www.cnblogs.com/arnoldlu/p/6253665.html 测试环境:AOSP 7.1.1+Kernel 4.4.17 HW:HiKey Ubuntu 14. ...

  9. range与xrange的区别

    一.Python中range()与xrange()有什么区别 range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列 rang ...

随机推荐

  1. SQL Server AlwaysOn架构及原理

    SQL Server AlwaysOn架构及原理 SQL Server2012所支持的AlwaysOn技术集中了故障转移群集.数据库镜像和日志传送三者的优点,但又不相同.故障转移群集的单位是SQL实例 ...

  2. eclipse 相同变量高亮显示 颜色修改 变量着色

    问题描述:      在eclipse中使用快捷键或其他原因,不小心按错了,使得变量的高亮显示没了.   1.简单的办法:  网上搜了一下,原来是Toggle Mark Occurrences (Al ...

  3. Strict Standards: Only variables should be passed by reference

    <?php $tag="1 2 3 4 5 6 7"; $tag_sel = array_shift(explode(' ', $tag)); print_r($tag_se ...

  4. Centos7 安装MPlayer过程详解

    使用自带的totem基本上都无法播放视频,各种格式都不支持,令人无语.想到了MPlayer,为了看片,决定编译安装,过程真的是折腾.如图是自带的Totem播放提示,安装了解码还是无法播放,反正要找其他 ...

  5. M1事后分析汇报总结

    学霸网站项目Postmortem结果 设想和目标 1.       我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 学霸网站为计算机学习提供了一个网上基地,在这里你 ...

  6. three.js 相关概念

    1.什么是three.js? Three.js 是一个 3D JavaScript 库.Three.js 封装了底层的图形接口,使得程序员能够在无需掌握繁冗的图形学知识的情况下,也能用简单的代码实现三 ...

  7. 【洛谷P3398】仓鼠找sugar

    画个图就能多少看出些规律 证明借鉴一下大牛的题解: 设从A到B,经过的深度最小的点为X 同理,C,D的为Y 题目是一个点从A出发到B 一个从C出发到D 那么从A到B可以分解成 先从A到X 再从X到B. ...

  8. document.write()

    以前一直以为document.write()就一定会清空文档里面的所有内容,一直没有去尝试,最近才知道原来是要在特定的情况下document.write才会清空文档里面所有内容的,在这里,觉得应该告诉 ...

  9. maven工程引用外部jar包

    maven工程经常回遇到引用外部jar包,需要先安装在jar包,然后再在工程中pom.xml文件中添加依赖. 示例: 命令行中运行: mvn install:install-file -Dfile=E ...

  10. 用scala实现一个sql执行引擎-(上)

    前言 在实时计算中,通常是从队列中收集原始数据,这种原始数据在内存中通常是一个java bean,把数据收集过来以后,通常会把数据落地到数据库,供后面的ETL使用.举个一个简单的例子,对一个游戏来说, ...