从range和xrange的性能对比到yield关键字(中)
上节提出了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关键字(中)的更多相关文章
- 从range和xrange的性能对比到yield关键字(上)
使用xrange 当我们获取某个数量的循环时,我们惯用的手法是for循环和range函数,例如: for i in range(10): print i 这里range(10)生成了一个长度为10 ...
- WPF DataGrid与ListView性能对比与场景选择
开门见山的说 性能对比: 在Demo中,DataGrid与ListView默认开启虚拟化(可以理解为动态渲染,类似懒加载只渲染屏幕可以看见的地方) DataGrid渲染10列50行随机字符280ms ...
- [Python]range与xrange用法对比
[整理内容]具体如下: 先来看如下示例:>>>x=xrange(0,8)>>> print xxrange(8)>>>print x[0]0> ...
- 实验比较python中的range和xrange
1 结论: 全用xrange,除非你需要使用返回的列表 2 实验一:性能对比 实验环境:win7 ,64位系统 python2.7 import time StartTime=time.time() ...
- Python从题目中学习:range()和xrange()
近期给公司培训Python,好好啃了啃书本,查了查资料,总结一些知识点. --------------------------------------------------------------- ...
- python 中range与xrange的区别
先来看看range与xrange的用法介绍 help(range)Help on built-in function range in module __builtin__: range(...) r ...
- range和xrange的区别详解
两种用法介绍如下:1.range([start], stop[, step])返回等差数列.构建等差数列,起点是start,终点是stop,但不包含stop,公差是step.start和step是可选 ...
- 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. ...
- range与xrange的区别
一.Python中range()与xrange()有什么区别 range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列 rang ...
随机推荐
- rem 和 ::
-------siwuxie095 rem 和 :: 都是用作批处理注解(等同于各种编程语言中的注释) 注解批处理时,标准写法是写在被注解代码的上一行 REM 在批处理文件或CONFIG.SYS里 ...
- 第一章 tomcat安装与启动
一.安装 1.下载tomcat安装包 2.解压安装包 3.配置环境变量 打开~/.bash_profile文件,输入一下两句话: export TOMCAT_HOME=/Users/enniu1/De ...
- Web 登陆界面---简单模块1
今天学习登陆界面的基本模块,其中几个基本的元素 a元素.form元素.布局元素(table\tr\td).加粗(b).input元素 1.<a href="">内容&l ...
- xsltproc docbook 转 html
/etc/xml/catalog <?xml version="1.0" encoding="UTF-8"?> <catalog xmlns= ...
- three.js 源码注释(三十九)Light/HemisphereLight.js 半球光、 自然光(天光效果)
/*** * HemisphereLight类 是在场景中创建半球光,就是天光效果,经常用在室外,将各个位置的物体都照亮,室内的光线大多是方向性的, * 无论是窗口还是灯槽,用平面光很方便,室外用平面 ...
- 巧用Javascript中的slice()
slice()是Javascript中Array的一个方法,定义是这样的. arrayObject.slice(start,end)作用是从原数组中从start到end位置截取出一个新的数组,返回值是 ...
- 【洛谷P1969】积木大赛
递增区间内累计增量 #include<cstdio> #include<cstring> using namespace std; int a,h,n,ans; int mai ...
- SQL语句一次INSERT多条记录的方法
1.SQL Server或MySQL中: INSERT INTO `userTable` (`user_id`, `user_name`) VALUES (, 'dsf'), (, 'fgy'), ( ...
- JMeter入门
下载及安装 下载地址:http://jmeter.apache.org/download_jmeter.cgi 直接下载Release版本,解压即可使用 MAC.Linux中直接运行:jmeter文件 ...
- sscanf函数用法详解
sscanf() - 从一个字符串中读进与指定格式相符的数据. 函数原型: Int sscanf( string str, string fmt, mixed var1, mixed var2 ... ...