博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从range和xrange的性能对比到yield关键字(中)
阅读量:6555 次
发布时间:2019-06-24

本文共 3313 字,大约阅读时间需要 11 分钟。

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

 

yield的使用

 

我们看下面的程序:

#coding: utf-8def test():    print 4    print 2    print 5if __name__ == '__main__':    test()

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

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

#coding: utf-8def test():    yield 4    yield 2    yield 5if __name__ == '__main__':    print test()

运行结果有些奇怪:

我们尝试这样使用:

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

结果却出人意料:

wing@ubuntu:~/Documents/py|⇒  python 17.py425

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

实际上,原因并非如此。

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

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

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

尝试下面的代码:

#coding: utf-8def test():    yield 4    yield 2    yield 5if __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.py425Traceback (most recent call last):  File "17.py", line 14, in 
print it.next()StopIteration

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

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

 

range的实现

 

我们尝试实现range:

#coding: utf-8def _range(value):    i = 0    result = []    while i < value:        result.append(i)        i += 1    return resultif __name__ == '__main__':    for i in _range(4):        print i

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

 

xrange的模拟实现

 

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

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

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

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

 

为何xrange比range高效?

 

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

 

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

#coding: utf-8import sysfrom time import timedef _range(value):    i = 0    result = []    while i < value:        result.append(i)        i += 1    return resultdef _xrange(value):    i = 0    while i < value:        yield i        i += 1def 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_timedef test1(length):    for i in _range(length):        pass@count_timedef test2(length):    for i in _xrange(length):        passif __name__ == '__main__':    length = int(sys.argv[1])    test1(length)    test2(length)

运行结果为:

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

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

 

下文,我们探究生成器。

转载于:https://www.cnblogs.com/inevermore/p/4224985.html

你可能感兴趣的文章
Hadoop单机模式安装-(3)安装和配置Hadoop
查看>>
$.extend({},defaults, options) --(初体验三)
查看>>
自己主动瀑布流布局和实现代码加载
查看>>
maven的一些依赖
查看>>
腾讯云短信服务使用记录与.NET Core C#代码分享
查看>>
jQuery hover() 方法
查看>>
sql语句
查看>>
android 一步一步教你集成tinker(热修复)
查看>>
到底有多少内存
查看>>
centos7.3 安装ovirt-engine4.0 版本
查看>>
putty、xshell的密钥认证
查看>>
Jenkins+git+tomcat 自动化持续部署
查看>>
项目log日志打印
查看>>
Openstack的环境的Mitaka部署环境服务,实例(1)
查看>>
Redis总结(七)Redis运维常用命令
查看>>
常用shell
查看>>
文档的压缩与打包
查看>>
python3 在不同操作系统安装第三方库方法
查看>>
redhat5.8+mfs(提供软件包文档)
查看>>
python编写登录接口
查看>>