上节提出了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 Express LocalDBB

    介绍一种改进的SQL Express LocalDB LocalDB专门为开发商.它是非常容易安装,无需管理,但它提供了相同的T-SQL语言,编程表面和客户端供应商定期的SQL Server Expr ...

  2. ffffffuzzzzzzzzzzzzing

    为了克服现有 Fuzzing 技术对目标程序缺乏理解.测试完全随机且盲目的缺点,提出了新方法:基于动态符号执行的智能测试技术. 先通过静态分析获得危险点,然后通过符号执行收集路径条件,最后利用约束求解 ...

  3. POJ 2186-Popular Cows (图论-强联通分量Korasaju算法)

    题目链接:http://poj.org/problem?id=2186 题目大意:有n头牛和m对关系, 每一对关系有两个数(a, b)代表a牛认为b牛是“受欢迎”的,且这种关系具有传递性, 如果a牛认 ...

  4. c语言检测文件是否存在int __cdecl access(const char *, int);

    最近写代码,遇到很多地方需要判断文件是否存在的.网上的方法也是千奇百怪,“百家争鸣”. fopen方式打开的比较多见,也有其他各种方式判断文件是否存在的,由于其他方法与本文无关,所以不打算提及. 笔者 ...

  5. workerman 的回调函数

    接下来,记录一下workerman 的回调函数 <?php /** * Created by PhpStorm. * User: zeopean * Date: 2016-08-26 * Tim ...

  6. Visual Studio Professional 2015 key

    Visual Studio Professional 2015 Key : HMGNV-WCYXV-X7G9W-YCX63-B98R2 Visual Studio Enterprise 2015 Ke ...

  7. Problem 2136 取糖果---FUOJ (线段树+维护)

    http://acm.fzu.edu.cn/problem.php?pid=2136 题目大意: 给你n个袋子每个袋子里都装有糖果,然后呢你可以每次抽取一个连续的一个区间的袋子,然后带走里面最多糖果数 ...

  8. StringGrid 实例5 本例功能:字体修改为居中,红色,20号

    实例5 本例功能: 在FireMonkey中StringGrid对于字体的调整由于没有font属性和onDrawCell事件使得变得有些麻烦,后来费了一些功夫才找到方法.但由于XE3中某些属性还是没有 ...

  9. 如何让chrome始终运行插件

    使用chrome可能有时候会拦截比如阿里旺旺和腾讯等的登录插件,那么怎么才始终允许,而不需要每次确认呢.下面. 1. 打开Chrome浏览器. 在地址栏中输入 chrome://plugins 回车 ...

  10. HashMap源码解析

    本文转载摘录自http://www.importnew.com/20386.html Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashM ...