玥殇 发表于 2018-8-7 08:14:37

Python——对生成器的一点小测试

"""  
1、生成器就是对象
  
2、每次调用next()方法时就返回一个值,直到抛出StopIteration异常
  
3、如何创建生成器?很简单,只需写一个普通的函数并包含yield语句,而不是return语句,因此,python会自动将这个函数标记为生成器
  
4、yield语句的主要作用是和return语句一样返回一个值,但最重要也是最需要明白的是,yield语句返回值后,解释器会保存对栈的引用
  
    它将被用来在下一次调用next()时回复函数的执行
  
"""
  

  
#创建一个生成器
  
def mygen():
  
    yield 1
  
    yield 2
  
    yield "a"
  
    yield "b"
  

  
# print(mygen()) #此刻,打印出来的是一个生成器对象
  
# g = mygen()
  
# print(next(g))
  
# print(next(g))
  
# print(next(g))
  
# print(next(g))
  
#print(next(g)) #已结到达最后一个数据了,如果还继续打印则抛出StopIteration异常
  

  
#检查一个函数是否是一个生成器,是的话返回true,不是的话返回false
  
# import inspect
  
# ret = inspect.isgeneratorfunction(mygen)
  
# print(ret)
  

  
#查看生成器的当前状态
  
"""
  
有下面几种状态:
  
    1、GEN_CREATED:正在等待第一次被执行
  
    2、GEN_RUNNING:当前正在被解析器执行
  
    3、GEN_SUSPENDED:等待被next()调用唤醒
  
    4、GEN_CLOSED:已结结束运行
  
"""
  
#测试一下,是如何查看这些状态的
  
import inspect
  
def mygen2():
  
    yield 1
  
gens = mygen2()
  
print(inspect.getgeneratorstate(gens)) #第一次查看状态是,GEN_CREATED(正在等待第一次被执行)
  
print(next(gens)) #通过next()调用一次
  
print(inspect.getgeneratorstate(gens)) #此时的状态是GEN_SUSPENDED(等待被next()调用唤醒)
  
try:
  
    print(next(gens)) #再next()一次的话,因没有值了所以引发了一个StopIteration异常
  
except StopIteration:
  
    pass
  
print(inspect.getgeneratorstate(gens)) #那么,它的状态是GEN_CLOSED(已结结束运行)
  

  
#那么生成器的应用场景是啥?看个例子
  
"""
  
在我的ubuntu操作系统环境
  
root@udesk:~# ulimit -v 131072 #限制运行内存在128M
  
root@udesk:~# python3
  
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
  
on linux
  
Type "help", "copyright", "credits" or "license" for more information.
  
>>> a = list(range(10000000))
  
Traceback (most recent call last):
  
File &quot;<stdin>&quot;, line 1, in <module>
  
MemoryError #内存错误
  
>>>
  

  
#用生成器的方式试试
  
>>> for v in range(10000000):
  
...   if v == 50000:
  
...         print(&quot;found ok&quot;)
  
...         break
  
...
  
found ok
  

  
奇怪的是,我左看右看,看着都不像是生成器啊!
  
原因是:
  
    在python3中,range()函数会返回生成器,也就是说返回的是一个可迭代对象(这点一定要记住)
  
    所以,我只需要第50000个数字,生成器仅仅只会生成50000个数字,而不是像刚才
  
    那么样试图要生成10000000个数字,导致把内存给撑爆了
  

  
我在python2环境中测试了下,那么需要注意的问题有:
  
>>> xrange(10000000) #python2中xrange是用于返回生成器(也就是说不会马上生成,用的时候才会即时的生成)
  
xrange(10000000)
  
>>> range(10000000) #range获取生成器(也就是说会马上生成数字)
  
Traceback (most recent call last):
  
File &quot;<stdin>&quot;, line 1, in <module>
  
MemoryError
  

  
注意了:在python3中去除掉了xrange()函数,仅有range,python3中的range就是返回生成器
  

  
简单总结,用专家说的话就是:
  
    生成器运行通过即时生成的值以极少的内存消耗来应对大规模的数据集和循环处理
  
    任何时候想要操作大规模数据,生成器都可以帮助确保有效地对数据进行处理
  
&quot;&quot;&quot;
  

  
#再看一个例子
  
#一般的写法
  
def addlist(alist):
  
    r = []
  
    for i in alist:
  
      r.append(i + 1)
  
    return r
  
ret = addlist()
  
print(ret)
  

  
#通过yield的写法
  
def yieldlist(alist):
  
    for i in alist:
  
      yield i + 1
  
ret1 = addlist()
  
print(ret1)
  
#结果都是一样,那么数据量小看不出啥效果,要不你搞1000万条数据你再试试?效果就立马出来了
  

  
#最后关于send和next
  
&quot;&quot;&quot;
  
send()与next()的区别在于send可以传递参数给yield表达式,
  
这时传递的参数会作为yield表达式的值,而yield的参数是返回给调用者的值。
  
初始调用时必须先next()或send(None),否则会报错。
  
&quot;&quot;&quot;
  
#下面看个例子:我想实现生产一些IP地址,并发送给另外一个函数干活
  
def jobs():
  
    while True:
  
      ip = yield #yiend接收send发送过来的ip
  
      print(&quot;ssh connection:{},Start power off...&quot;.format(ip))#开始干活
  

  
#生产IP地址
  
def production_ip():
  
    j = jobs()
  
    next(j) #初始调用时必须先next()
  
    for i in range(10):
  
      ip = &quot;192.168.89.{0}&quot;.format(i) #生产IP地址
  
      j.send(ip) #将IP地址发生给干活的函数jobs
  

  
production_ip()
页: [1]
查看完整版本: Python——对生成器的一点小测试