从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 ...
随机推荐
- Android之activity初讲
前天我们已经成功创建了第一个Android项目,如果我要把软件推荐给我的第一个用户,毫无疑问是从界面开始介绍.因为即使你的程序算法再高效,架构再出色,用户根本不会在乎这些,他们一开始只会对看到的东西感 ...
- oracle学习-存储过程返回一个值,和返回一个结果集
一.返回一个值 --创建存储过程 create or replace procedure sp_hu_test(spcode in varchar2,spname out varchar2)is be ...
- [C#]循环输出 000 - 999999
循环输出 000 - 999999 ; i < ; i++) { , i.ToString().Length); j < 7; j++) { Debug.WriteLine(i.ToStr ...
- Linux crontab命令的使用方法
crontab命令常见于Unix和Linux的操作系统之中,用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于"crontab"文件中,以供之后读取和执行. 在 ...
- zabbix使用host metadata方式主动注册
host metadata是zabbix2.2新增加的功能,该功能在zabbix-agent端可以自定义条件,在选择自动注册的时候,zabbix-server端可以根据host metadata来选择 ...
- 剑指offer题目61-67
面试题61:把二叉树打印成多行 public class Solution { public ArrayList<ArrayList<Integer> > Print(Tree ...
- 10个你必须掌握的Linux超酷VI命令技巧
大部分Linux开发者对vi命令相当熟悉,可是遗憾的是,大部分开发者都只能掌握一些最常用的Linux vi命令,下面介绍的10个vi命令虽然很多不为人知,但是在实际应用中又能让你大大提高效率. 在使用 ...
- Observer设计模式中-委托事件-应用在消息在窗体上显示
Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...
- RAID配置
一.madam -a 检测设备名称 -n 指定硬盘数量 -l 指定raid级别 -C 创建 -f 模拟硬盘故障 -r 移除硬盘 -a ...
- 解决Android中多次点击启动多个相同界面的问题
在Android开发过程中我们经常会碰到这样的问题,当用户点击一个View启动一个新的Activity的时候,如果快速地多次点击就会启动多个相同的界面.虽然说很少会有用户这么玩自己的手机,但是一旦出现 ...