从零开始的Linux运维屌丝之路,资源免费分享平台   运维人员首选:简单、易用、高效、安全、稳定、社区活跃的开源软件
  • 首页
  • Python
  • 29、python函数进阶、生成器、迭代器

29、python函数进阶、生成器、迭代器

发布:蔺要红05-14分类: Python


函数生成器         迭代器

减少代码量,只能写到列表和元祖里,

 
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b = []
>>> for i in a:b.append(i+1)
... 
>>> b
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a = b
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#差评,如果数据量多,内存里有两份列表,

优化-函数 列表生成试
 
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a = [ i*i for i in a ] #循环a然后相乘
>>> a
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
#a可以是元祖,字符串,等等等
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a = [ i+i if i<5 else i*i for i in a ] #利用三元运算 #循环a,然>5相乘 <5相加
>>> a
[0, 2, 4, 6, 8, 25, 36, 49, 64, 81]
>>> a = 'Linyaohong'  #循环字符串
>>> a =  [ i for i in a]
>>> a
['L', 'i', 'n', 'y', 'a', 'o', 'h', 'o', 'n', 'g']


函数生成器
 
只有next调用
只能按照顺序往前走,不能后退、
当生成到最后的时候会报错
 
 
>>> a2 = (i for i in range(1000))
>>> a2 #a2为生成器、而不是以一个1-1000的列表形式存在内存里
<generator object <genexpr> at 0x7f52cf926ca8>
>>> next(a2)
0
>>> next(a2)
1
>>> next(a2)
2
>>> next(a2)
3

用for循环调用生成器(遇到错终止循环),而where循环会报错
 
>>> a2 = (i for i in range(5))  #在这里range(5)是一个生成器、生成[1,2,3,4,5]
>>> for n in a2:
...  print(n)
... 
0
1
2
3
4

#while循环

>>> a2 = (i for i in range(5)) #在这里range(5)是一个生成器、生成[1,2,3,4,5]
>>> while True:
...  next(a2)
... 
0
1
2
3
4
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
StopIteration


当需要一个1-1000的列表时、range(10000)  
函数生成器range(10000),会先生成一个公式 range(0, 10000)
当需要调用的时候,按照规律依次把数据取出、不会先把1-10000的列表存在内存中,

在python2中
 

>>> range(5)
[0, 1, 2, 3, 4]
>>> xrange(5)
xrange(5)

 

 
python3中
 

>>> range(10)
range(0, 10)

 

range就是用生成器的模式实现



斐波那契数列即著名的兔子数列:1、1、2、3、5、8、13、21、34、……
 
>>> a,b
(1, 2)
>>> a,b = b,a+b #a+b 是原来的a    +b
>>> a,b
(2, 3)
#
>>> a=1
>>> b=2
>>> a = b
>>> b =a+b
>>> a,b
(2, 4)
#虽然a,b = b,a+b 的含义是 a=b  b=a+b 但是和 a,b = b,a+b是有区别的
 
# -*- coding:utf-8 -*-

def func(max):
    n,a,b = 0,0,1
    while n < max:
        print(b)
        a,b = b,a+b
        n += 1
    return 'done'
func(5)

#D:\python\python.exe F:/运维笔记/python/函数/函数进阶-斐波那契.py
1
1
2
3
5

改变
 
# -*- coding:utf-8 -*-
def func(max):
    n,a,b = 0,0,1
    while n < max:
        yield b  #把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
        a,b = b,a+b
        n += 1
    return 'done'
f = func(6)

for i in f:  #用for循环来执行
    print(i)

#D:\python\python.exe F:/运维笔记/python/函数/函数进阶-斐波那契.py
1
1
2
3
5
8
或者
def func(max):
    n,a,b = 0,0,1
    while n < max:
        yield b #把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
        print(b)
        a,b = b,a+b
        n += 1
    return 'done'
f = func(10)
next(f)        #用next

#D:\python\python.exe F:/运维笔记/python/函数/函数进阶-斐波那契.py


#第一次调用执行到yield b停止
验证 yield  把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
def func(max):
    n,a,b = 0,0,1
    while n < max:
        print("yield上面")
        yield b #把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
        print(b)
        print("yield下面")
        a,b = b,a+b
        n += 1
    return 'done'
f = func(10)
next(f)
next(f)

#D:\python\python.exe F:/运维笔记/python/函数/函数进阶-斐波那契.py
yield上面
1
yield下面
yield上面

生成器的创建方式:列表 生成试(),简单的三元运算    或   函数


创建函数生成器
 
# -*- coding:utf-8 -*-
def  lin(n):
    count = 0
    while count < n:
        print(count)
        count += 1
        yield count  #把count返回给f=lin(10)、并冻结当前的执行过程,next让函数继续执行
f = lin(10)
print(lin(10))
next(f)
next(f)
f.send(None) #或者用次方法调用
f.send(None) #或者用次方法调用
f.__next__()  #或者用次方法调用
f.__next__()  #或者用次方法调用


#D:\python\python.exe F:/运维笔记/python/函数/函数-进阶函数-函数生成器.py
<generator object lin at 0x0000027BC59DC480>
0
1
2
3
4
5
#generator  生成器

应用场景:可以把函数运行的步骤,按照需求返回给外部、然后再继续运行函数
比如:当读取一个很大的文本时,生成器可以按行数返回给用户,而不是一次性读出返回给用户(时间长),然后再次调用生成器,继续读取下面的文本返回给用户

 




函数中有 yield之后,就得到了一个 函数生成器、
return在函数生成器里,代表生成器终止,会报错

 
# -*- coding:utf-8 -*-
def  lin(n):
    count = 0
    while count < n:
        print(count)
        count += 1
        yield count  #把count返回给f=lin(10)、并冻结当前的执行过程,next让函数继续执行
    print('------------')
    return 666
f = lin(5)
print(lin(5))
next(f)
next(f)
next(f)
f.__next__()  #或者用此方法调用
f.__next__()  #或者用此方法调用
f.__next__()  #或者用此方法调用

#D:\python\python.exe F:/运维笔记/python/函数/函数-进阶函数-函数生成器.py
Traceback (most recent call last):
<generator object lin at 0x0000026EA7C9C480>
  File "F:/运维笔记/python/函数/函数-进阶函数-函数生成器.py", line 17, in <module>
0
1
2
3
    f.__next__()
4
------------
StopIteration: 666       #return 666  可以在通过别的方式 捕捉这个错误
 

new_lin.send("stop")
#1、唤醒并继续执行
#2、发送一个信息到生成器内部
#3、send发送信息不能为最后一个yield
#4、第一次使用生成器、必须使用next获取下一个值

 
# -*- coding:utf-8 -*-
def  lin(n):
    count = 0
    while count < n:
        print('count',count)
        count += 1
        sign = yield count  #把count返回给f=lin(10)、并冻结当前的执行过程,next让函数继续执行
        if sign == 'stop':
            print('-sign-', sign)
            break
        print('sign.......', sign)
    return 666
new_lin = lin(5)
n = next(new_lin)  # 和next(new_lin)一样
next(new_lin)
new_lin.send("stop")

#D:\python\python.exe F:/运维笔记/python/函数/函数-进阶函数-函数生成器.py
count 0
sign....... None
count 1
-sign- stop
Traceback (most recent call last):
  File "E:/整理版笔记/python/第二章/isalnum.py", line 27, in <module>
    new_lin.send("stop")
StopIteration: 666

Process finished with exit code 1


 




可以直接用于for循环的数据类型有

    1、集合数据类型:list,tuple,dict,set,str
    2、generator:包括yield和generator cunction

这些可以直接用做for循环的对象统称为:可迭代对象 Iterable,可以使用isinstance()判断一个对象是否是 Iterable
 
>>> from collections import Iterable
>>> Iterable
<class 'collections.abc.Iterable'>
>>> isinstance('abc',Iterable)
True
>>> isinstance('100',Iterable)
True
>>> isinstance(100,Iterable)  #数字不是
False
>>> isinstance({},Iterable)
True
>>> isinstance([],Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
 
而生成器不但可以用作用for循环,还可以被next()函数不断调用,并且返回下一个值,直到最后抛出StopIteration错误,表示无法继续了

可以被next()函数调用并不断返回下一个值、称为迭代器:iterator (生成器只是迭代器的一种,学了面向对象后,可以自己定义一个类,依然可以next())

 
>>> from collections import Iterator
>>> isinstance([],Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance((x for x in range(10)),Iterator)
True
>>> isinstance("abc",Iterator)
False
>>> isinstance(100,Iterator)
False

生成器都是Iterator对象,但是list,dict,str虽然是Iterable,但不是 Iterator

把list,dict,str等Iterable 变成iterator可以使用iter()函数

 
#把list变成迭代器
>>> isinstance("abc",Iterator)
False
>>> a = iter('abc')      
>>> a.__next__()
'a'
>>> a.__next__()
'b'
>>> a.__next__()
'c'
>>> a.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

为什么list、diet、str 等数据类型不是Iterator ?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,
直到没有数据时抛出Stoplteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,
只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的



凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型list、diet、str等是Iterable但不是Iterator ,不过可以通过 iter()函数获得一个Iterator对象



 python3中的for循环本质上就是通过不断调用next()函数来实现
 
>>> for x in [1,2,3,4,5]:
...  pass




 
温馨提示如有转载或引用以上内容之必要,敬请将本文链接作为出处标注,如有侵权我会在24小时之内删除!

欢迎使用手机扫描访问本站