上节提出了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. Android之activity初讲

    前天我们已经成功创建了第一个Android项目,如果我要把软件推荐给我的第一个用户,毫无疑问是从界面开始介绍.因为即使你的程序算法再高效,架构再出色,用户根本不会在乎这些,他们一开始只会对看到的东西感 ...

  2. .NET Remoting获取配置通道:

    接上文: public static string ChannelManagerUrl        {            get            {                retu ...

  3. 在将 varchar 值 '1,2,3,4,5,6,7,8' 转换成数据类型 int 时失败。

    alter PROCEDURE PrTradingDelete ) AS BEGIN WHERE id in(@id) END GO 执行上面这个存储过程会异常.提示 :在将 varchar 值 '1 ...

  4. jzoj[1224]

    怎么说呢,这道题的题面一看就知道是最小生成树,我是把二维数组转化为一维数组来做了,1000*1000没有超过一维数组的定义范围,不会爆栈 然后用并查集的kruskal来写就好了 首先一个start函数 ...

  5. PHP 输出缓冲控制(Output Control) 学习

    php 缓冲简介 其实我对php ob 系列印象还是很模糊,具体怎么玩的,还不是很了解,平时curd,确实对这些内容没有深入.作为phper 甚是惭愧.网上搜了一通,互相copy,代码运行不能出现作者 ...

  6. Caffe(卷积神经网络框架)介绍

    Caffe(卷积神经网络框架)Caffe,全称Convolution Architecture For Feature Extraction caffe是一个清晰,可读性高,快速的深度学习框架.作者是 ...

  7. DataTime格式化大全(转载)

    //c datetime 格式化 DateTime dt = DateTime.Now; Label1.Text = dt.ToString();//2005-11-5 13:21:25 Label2 ...

  8. 解Tom大叔出的Javascript题目

    原文参考,http://www.cnblogs.com/TomXu/archive/2012/02/10/2342098.html Tom大叔的博客深入理解Javascript系列真是我们学习Java ...

  9. android text

    "@you bang--- go on -------" 需要做分享内容,前面有段格式固定写死,同时颜色为灰色:后面的内容可以编辑,颜色为黑色,同时支持多行 有人用textview ...

  10. webstorm与SAE的svn仓库链接进行版本控制

    这里把我使用webstorm与SAE的svn仓库链接: 1.先得设置webstorm中的版本控制,File->Settings->Version Control->Subversio ...