python函数-迭代器和生成器

一 迭代器

1.1 认识迭代器

什么是迭代 什么是迭代器

迭代器
如何从列表、字典中取值的
    index索引 ,key
    for循环
凡是可以使用for循环取值的都是可迭代的
可迭代协议 :内部含有__iter__方法的都是可迭代的
迭代器协议 :内部含有__iter__方法和__next__方法的都是迭代器 
一个生成器 只能取一次生成器在不找它要值的时候始终不执行当他执行的时候,要以执行时候的所有变量值为准
l = [1,2,3]
while True:
    lst = l.__iter__()   ## iter 方法自带一个next方法,所以第一次可以取值输出,并取到下一个值保存
    print(next(lst))

l = [1,2,3]
lst = l.__iter__()          ##当输出最后一个值的时候,仍旧执行next方法会报错
while True:
    print(next(lst))

l = [1,2,3]
lst = l.__iter__()
while True:
    try:
        print(next(lst))     #  测试错误
    except StopIteration:    #   当遇到报错  StopIteration  的时候,执行break,停止执行next方法。


break

什么是迭代器  迭代器 = iter(可迭代的),自带一个__next__方法

可迭代 最大优势是 节省内存

我们可以从range学习

from collections import Iterable,Iterator   #可迭代的 迭代器
print(range(100000000))
print(isinstance(range(100000000),Iterable))   # True 是可迭代的
print(isinstance(range(100000000),Iterator))    # False  不是迭代器
py2 range 不管range多少 会生成一个列表 这个列表将用来存储所有的值py3 range 不管range多少 都不会实际的生成任何一个值
迭代器的优势:    节省内存    取一个值就能进行接下来的计算 ,而不需要等到所有的值都计算出来才开始接下来的运算 —— 快迭代器的特性:惰性运算

二 生成器

生成器,Genenator

2.1 什么是生成器

自己写的迭代器,就是一个生成器

写迭代器的两种机制:

生成器函数和生成器表达式

# 凡是带有yield的函数就是一个生成器函数
def func():
    print(‘****‘)
    yield 1
    print(‘^^^^‘)
    yield 2   # 记录当前所在的位置,等待下一次next来触发函数的状态
生成器函数的调用不会触发代码的执行,而是会返回一个生成器(迭代器)想要生成器函数执行,需要用next
g = func()   # g = 函数执行之后生成的一个生成器
print(‘---‘,g)  # 函数执行不输出任何内容
print(‘--‘,next(g))   # 调用一次next,会执行一次取值,到下一次遇到yield停止
print(‘--‘,next(g))

调用两次next返回的结果:

****
-- 1
^^^^
-- 2

2.1.2使用生成器监听文件输入

import time
def listen_file():
    with open(‘userinfo‘) as f:
        while True:
            line = f.readline()  #使用readline监听文件,它不会自动结束
            if line.strip():
                yield line.strip()
            time.sleep(0.1)

g = listen_file()
for line in g:
    print(line)

# 使用mac的时候,需要用echo向文件追加内容测试。win手动输入 使用ctrl+s保存即可

2.1.3 send关键字

理解send

def func():
    print(11111)
    ret1 = yield 1
    print(22222,‘ret1 :‘,ret1)
    ret2 = yield 2
    print(33333,‘ret2 :‘,ret2)
    yield 3

g = func()
ret = next(g)  # 第一次next,返回11111 和 yield 1 。并记录当前位置,等待下一个next来触发函数状态
print(ret)
print(g.send(‘alex‘))  # 在执行next的过程中 传递一个参数 给生成器函数的内部,同时接收yield的返回值。
print(g.send(‘金老板‘))

##接收send发送的参数的是上次次结束的yield。第一次执行next(g),后,返回yield 1。yield会记录当前所在位置,等待下一个next来触发函数的状态。当执行send(alex)的时候的时候,相当于又执行一个next,且yield 1首先执行,接收send的内容。
# send 不能在第一次next之前。第一次必须用next触发这个生成器,有一个激活的过程

send  不能用在生成器的第一步。必须先用next触发激活生成器

例子: 计算移动平均值

要求,每次给你一个数,都计算之前所有拿到的数的平均值。例如计算月度的天平均收入

def average():
    sum_money = 0
    day = 0
    avg = 0
    while True:
        money = yield avg
        sum_money += money
        day += 1
        avg = sum_money/day

g = average()
print(g.__next__())  #第一步 先触发生成器,之后才能send
print(g.send(20))
print(g.send(40))
print(g.send(60))

2.2 预激生成器

个人理解:生成器send的时候需要用next激活,才能使用send传值。这里用到的是装饰器来进行生成器函数的预激活

def init(func):
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        next(ret)   # 预激活
        return ret  #  需要返回生成器的结果给send
    return inner

@init
def average():
    sum_money = 0
    day = 0
    avg = 0
    while True:
        money = yield avg
        sum_money += money
        day += 1
        avg = sum_money/day
#
g = average()
print(g.send(200))
print(g.send(300))
print(g.send(600))

2.3 生成器的使用

2.3.1 yield from

# yield from
def generator_func():
    for i in range(5):
        yield i
    for j in ‘hello‘:
        yield j

def generator_func():
    yield from range(5)
    yield from ‘hello‘

以上两种写法完全是完全相同的

2.3.2 迭代器只能生成一次

我们看下这两段代码

# 定义函数
# # yield from
def generator_func():
    for i in range(5):
        yield i

# 代码调用第一种方式
g = generator_func()
while True:
    print(next(g))

#输出内容:
0
1
2
3
4
  File "/Users/wph/PycharmProjects/learn/Day4/8.复习练习.py", line 194, in <module>
    print(next(g))
StopIteration

# 代码调用第二种方式
while True:
    print(next(generator_func()))

# 执行结果是一直输出 0

我们想一下,为什么会出现这样的情况呢?

来继续看下面的代码:

def generator_func():
    for i in range(5):
        yield i

g1 = generator_func()
g2 = generator_func()
print(g1,g2)
#输出结果 <generator object generator_func at 0x104c2b0a0> <generator object generator_func at 0x104c2b0f8> 可以看到 g1和g2的内存地址是不一样的。也就是说每次调用generator_func,都会生成一个全新的生成器。那么每次从里面取值,都是会从头开始的。

2.3.3 生成器函数定义

生成器函数 是我们python程序员实现迭代器的一种手段

主要特种是 在函数中 含有 yield

调用一个生成器函数 不会执行这个函数中的代码,知识会获得一个生成器(迭代器)

只有从生成器中取值的时候,才会执行函数内部的代码。且每获取一个数据才执行得到这个数据的代码

获取数据的方式包括 next send 循环 数据类型的强制转换

yield返回值的简便方法,如果本身就是一个可迭代的,且要把可迭代数据中的每一个元素都返回,可以用 yield from

使用send的时候,在生成器创造出来自后要进行预激,这一步可以使用装饰器完成。

生成器的特点:节省内存 惰性计算

生成器用来解决内存问题和程序功能之间的解耦

2.3.4 生成器表达式

先了解一下列表推导式:

# 列表推导式
new_lst = []
for i in range(10):
    new_lst.append(i**2)
print(new_lst)

print([i**2 for i in range(10)])

# 求列表中的数对2取余
l = [1,2,3,-5,6,20,-7]
print([i%2 for i in range(10)])

# 取出列表中的奇数
l = [1,2,3,-5,6,20,-7]
print([num for num in l if num%2 == 1])

30以内所有能被3整除的数
print([i for i in range(30) if i%3 ==0])

30以内所有能被3整除的数的平方
print([i**2 for i in range(30) if i%3 ==0])

找到嵌套列表中名字含有两个‘e’的所有名字
names = [[‘Tom‘, ‘Billy‘, ‘Jefferson‘, ‘Andrew‘, ‘Wesley‘, ‘Steven‘, ‘Joe‘],
         [‘Alice‘, ‘Jill‘, ‘Ana‘, ‘Wendy‘, ‘Jennifer‘, ‘Sherry‘, ‘Eva‘]]
print([name for name_lst in names for name in name_lst if name.count(‘e‘) == 2])

生成器表达式

l = [1,2,3,-5,6,20,-7]
30以内所有能被3整除的数

l = [i for i in range(30) if i%3 ==0]   # 列表推导式 排序的时候
g = (i for i in range(30) if i%3 ==0)   # 生成器表达式 庞大数据量的时候 使用生成器表达式   #与列表推导式相比,只是把[]换成了()
print(l) #结果是一个列表
print(g) ## 结果只是拿到一个生成器  <generator object <genexpr> at 0x1043100a0>
for i in g:print(i)  # 从这个生成器中取值

总结:

1. 把列表解析(列表推导式)的[]换成()得到的就是生成器表达式

2. 列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存。

3. Python不但使用迭代器协议,让for循环更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如,sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所有,我们可以直接这样计算一系列值的和

print(sum(x ** 2 for x in range(4)))

2.3.5 生成器面试题

 # 面试题  请确认这段代码的打印内容
def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)   ### 生成器表达式 不执行!
g2=(i for i in g1)

print(list(g1))  #执行生成器表达式  #结果  [0,1,2,3]
print(list(g2))                  #结果  []

解析:因为g=demo()的使用调用生成器函数,g是一个生成器。g1 是一个生成器推导式,并不会执行。

当print从g1 中取值完成后,由于一个生成器只能取值一次。,g2取不到值了。

面试题2: 很蛋疼的,慢慢看吧 ,不信你能懂。。(重点在于 生成器是什么时候执行的)

# 判断下列代码输出内容

def add(n,i):
    return n+i

def test():
    for i in range(4):
        yield i

g=test()
for n in [1,3,10]:
    g=(add(n,i) for i in g)

print(list(g))

输出内容

10 11 12 13

2.3.5 生成器准则

一个生成器 只能取值一次。

生成器在不找他要值的时候,始终不执行

当他执行的时候,要以执行时的所有变量值为准 

原文地址:https://www.cnblogs.com/wangph/p/8946609.html

时间: 2024-12-08 22:35:06

python函数-迭代器和生成器的相关文章

python函数(迭代器,生成器)

迭代器 索引:1 = [1,2,3] for循环:for i in l: i print(dir([]))告诉列表拥有的所有方法 只要是能被for循环的数据类型 就一定拥有__iter__方法print([].__iter__())一个列表执行了__iter__()之后的返回值就是一个迭代器 print(dir([])) print(dir([].__iter__())) print(set(dir([].__iter__())) - set(dir([]))) Iterable 可迭代的 --

python之迭代器与生成器

python之迭代器与生成器 可迭代 假如现在有一个列表,有一个int类型的12345.我们循环输出. list=[1,2,3,4,5] for i in list: print(i) for i in 12345: print(i) 结果: Traceback (most recent call last): File "C:/Pycham/生成器与迭代器/test1.py", line 6, in <module> for i in 12345: TypeError:

python基础----迭代器、生成器、协程函数

一.什么是迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法) 3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象. 二,为什么要用迭代器 优点: 1:迭代器提供了一种不依赖于索引的取值方式,

Python 函数对象、生成器 、装饰器、迭代器、闭包函数

一.函数对象 正确理解 Python函数,能够帮助我们更好地理解 Python 装饰器.匿名函数(lambda).函数式编程等高阶技术. 函数(Function)作为程序语言中不可或缺的一部分,太稀松平常了.但函数作为第一类对象(First-Class Object)却是 Python 函数的一大特性.那到底什么是第一类对象(First-Class Object)呢? 在 Python 中万物皆为对象,函数作为第一类对象有如下特性: #函数身为一个对象,拥有对象模型的三个通用属性:id(内存地址

Python的迭代器和生成器

先说迭代器,对于string.list.dict.tuple等这类容器对象,使用for循环遍历是很方便的就,在后台for语句对容器对象对象调用iteration()函数,这是python的内置函数,iter()会返回一个定义next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数.在没有后续元素是,调用next()会抛出一个StopIteration异常 上面说的都是python自带的容器对象,它们都实现了相应的迭代器方法,自定义类的遍历怎么实现,方法是

Python的迭代器与生成器

Python中的生成器和迭代器方便好用,但是平时对生成器和迭代器的特性掌握的不是很到位,今天将这方面的知识整理一下. 迭代器 为了更好的理解迭代器和生成,我们需要简单的回顾一下迭代器协议的概念. 迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法) 3.协议是一种约定,可迭代对象

Python之迭代器、生成器、装饰器和递归

一.迭代器&生成器 1.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束. 迭代器只能往前不会后退,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素.迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁.这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件 特点: 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容 不能随机访问集合中的某个值 ,只能从头

python中迭代器和生成器。

前言:很多python教程中,对python的解释不容易理解,本文记录自己的理解和体会,是对迭代器和生成器的初步理解. 迭代器: 迭代器的实质是实现了next()方法的对象,常见的元组.列表.字典都是迭代器. 迭代器中重点关注两种方法: __iter__方法:返回迭代器自身.可以通过python内建函数iter()调用. __next__方法:当next方法被调用的时候,迭代器会返回它的下一个值,如果next方法被调用,但迭代器没有只可以返回,就会引发一个StopIteration异常.该方法可

【Python】迭代器、生成器、yield单线程异步并发实现详解

转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从理论+程序调试验证的方式详细讲解这部分知识,话不多说,直接进入主题. 一.迭代器(Iterater):     首先介绍迭代器,迭代器是访问集合元素的一种方式,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.是不是觉得跟for循环很像?但是迭代器有几个特性需记住:    1.访问者