流畅的Python(十四)-可迭代对象、迭代器和生成器

本文详细介绍了Python中迭代器、生成器函数的工作原理,包括如何使用内置函数处理可迭代对象、经典迭代器模式、生成器表达式的使用以及yieldfrom语句的应用。此外,还涵盖了等差数列生成器、可迭代的归约函数和迭代器哨值的概念和示例。
摘要由CSDN通过智能技术生成

一、核心要义

1. 使用iter内置函数处理可迭代对象的方式

2. 如何使用Python实现经典的迭代器模式

3. 生成器函数工作原理

4. 使用生成器函数或生成器表达式代替经典的迭代器

5. 使用yield from语句合并生成器

二、代码示例

1、遍历单词序列回顾

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/26 20:42
# @Author  : Maple
# @File    : 01-遍历单词序列回顾.py
# @Software: PyCharm
from collections import abc
import re
import reprlib

RE_WORDS = re.compile('\w+')
class Sentence:

    def __init__(self,text):
        self.text =  text
        self.words = RE_WORDS.findall(text)

    def __getitem__(self, pos):
        return self.words[pos]

    def __len__(self):
        return len(self.words)

    def __repr__(self):
        return 'Sentences(%s) ' % reprlib.repr(self.text)

class Foo:
    def __iter__(self):
        pass

if __name__ == '__main__':

    # 1. 迭代Sentence对象
    s = Sentence('I love this word')
    """s可迭代的原因
    1. 当迭代一个对象的时候,解释器会检查对象是否实现了__iter__方法,如果实现了,就调用它,获取一个迭代器
    2. 如果没有实现__iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素
    3. 如果尝试失败,Python抛出TypeError异常,通常会提示"C object is not itereble"
    
    """
    for word in s:
        """
        I
        love
        this
        word
        """
        print(word)

    # 2. Sentence对象是否可通过issubclass和isinstance测试
    # 虽然Sentence对象实现了 __getitem__,因为是可迭代的,但其实无法通过测试
    # 所以如果要判断一个对象是否可迭代,最好不要使用该方式,而是直接iter(对象),如果没有报错,就说明对象是可迭代的
    print(issubclass(Sentence,abc.Iterable)) # False
    print(isinstance(s,abc.Iterable)) # False

    ## 获取迭代器
    s = iter(s)
    ## 迭代迭代器中的元素
    for word in s:
        print(word)

    # 3.一个类只要实现了__iter__方法,那么其对象就是可迭代的,并且可以通过issubclass和isinstance测试
    f = Foo()
    print(issubclass(Foo, abc.Iterable))  # True
    print(isinstance(f, abc.Iterable))  # True

2、可迭代对象和迭代器

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/26 21:03
# @Author  : Maple
# @File    : 02-可迭代对象和迭代器.py
# @Software: PyCharm


"""
两者的定义和关系:
 1.可迭代对象: 实现了__iter__方法
 2.迭代器: 实现了__iter__和__next__方法


"""
import re
import reprlib

RE_WORDS = re.compile('\w+')

"""
1.利用__iter__方法定义一个类,当调用iter方法时,返回一个迭代器
2.对迭代器进行遍历的时候,不断调用next方法,返回迭代器中的每一个元素
"""
class Sentence:

    def __init__(self,text):
        self.text =  text
        self.words = RE_WORDS.findall(text)

    def __iter__(self):
        return SentenceIterator(self.words)

    def __len__(self):
        return len(self.words)

    def __repr__(self):
        return 'Sentences(%s) ' % reprlib.repr(self.text)

class SentenceIterator:
    """
    定义迭代器:__iter__方法和__next__方法
    """

    def __init__(self,words):
        self.words = words
        self.index = 0

    def __iter__(self):
        """迭代器应该一直可以迭代,所以返回自身,在哪里会用到这个特性? """
        return self

    def __next__(self):
        try:
            word = self.words[self.index]
        except IndexError:
            raise StopIteration()
        self.index += 1
        return word


if __name__ == '__main__':

    # 1. Sentence测试
    s = Sentence('I love this world')
    for word in s:
        print(s)

    # 2.s2是一个迭代器
    ## 2-1 直接调用迭代器类 生成一个迭代器
    print('******2-1 直接调用迭代器类 生成一个迭代器************')
    s2 = SentenceIterator(['we', 'are', 'the', 'world'])
    print('s2:',s2)
    index = 0
    for word in s2:
        """we
           are
        """
        index += 1
        if index > 1:
            print(word)
            break
        else:print(word)


    ## 2-2 调用迭代器的iter方法,返回迭代器本身
    print('******2-2 调用迭代器的iter方法,返回迭代器本身************')
    s3 = iter(s2)
    print('s3:',s3)
    for word in s3:
        """
        在2-1的基础上,继续迭代,直到迭代结束
        the
        world
        """
        print(word)

3、生成器函数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/26 21:42
# @Author  : Maple
# @File    : 03-生成器函数.py
# @Software: PyCharm

"""
对上一小节的Sentence进一步改造:
   __iter__方法不用返回一个迭代器.而是直接将其定义为一个生成器函数
--什么是生成器函数: 包含yield关键
"""
import re
import reprlib

RE_WORDS = re.compile('\w+')

# 利用生成器函数,实现Sentence类对象的可迭代(不用在__iter__方法中返回迭代器)
class Sentence1:

    def __init__(self,text):
        self.text =  text
        self.words = RE_WORDS.findall(text)

    def __repr__(self):
        return 'Sentences(%s) ' % reprlib.repr(self.text)

    def __iter__(self):
        for word in self.words:
            yield word
        return

    def __len__(self):
        return len(self.words)

# 对Sentence1进一步改造:self.words不需要提前生成(是一个列表)
class Sentence2:

    def __init__(self,text):
        self.text = text

    def __repr__(self):
        return 'Sentences(%s) ' % reprlib.repr(self.text)

    def __iter__(self):

        # RE_WORDS.finditer(self.text)返回的是一个迭代器
        # 通过for循环遍历迭代器
        for match in RE_WORDS.finditer(self.text):
            yield match.group()
        return

# 利用生成器表达式,进一步改造Sentence
class Sentence3:

    def __init__(self,text):
        self.text = text

    def __repr__(self):
        return 'Sentences(%s) ' % reprlib.repr(self.text)

    def __iter__(self):
        # 直接返回生成器表达式,与yield的作用相同
        return (match.group() for  match in RE_WORDS.finditer(self.text))


if __name__ == '__main__':

    # 1.Sentence1测试
    s = Sentence1('I love this world')
    ##  调用iter返回一个生成器
    s_iter = iter(s)
    ## 遍历生成器(本质就是一个迭代器)
    for word in s_iter:
        """
        I
        love
        this
        word
        """
        print(word)

    # 2.Sentence2测试
    print('******2.Sentence2测试***********')
    s2 = Sentence2('Please stay with me forerver')
    ##  调用iter返回一个生成器
    s_iter2 = iter(s2)
    ## 遍历生成器(本质就是一个迭代器)
    for word in s_iter2:
        """
        Please
        stay
        with
        me
        forerver
        """
        print(word)

    # 3.Sentence3测试
    print('******3.Sentence3测试***********')
    s3 = Sentence3("Let's start study Python")
    ##  调用iter返回一个生成器
    s_iter3 = iter(s3)
    ## 遍历生成器(本质就是一个迭代器)
    for word in s_iter3:
        """
        Let
        s
        start
        study
        Python
        """
        print(word)

4、等差数列生成器

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/27 20:03
# @Author  : Maple
# @File    : 04-等差数列生成器.py
# @Software: PyCharm



# 自定义等差序列生成器
from decimal import Decimal

class ArithmeticProgression:

    def __init__(self,start,step,end=None):
        self.start = start
        self.step = step
        self.end = end

    def __iter__(self):

        index = 0
        result = type(self.start + self.step)(self.start)
        forever = self.end is None

        while forever or result < self.end:
                yield result
                index += 1
                result = self.start + self.step * index

class ArithmeticProgression2:

    def __init__(self,start,step,end=None):
        self.start = start
        self.step = step
        self.end = end

    def __iter__(self):

        ap_gen = itertools.count(self.start, self.step)
        if self.end is not None:
            ap_gen =  itertools.takewhile(lambda n: n < self.end, itertools.count(self.start, self.step))

        # 直接返回迭代器
        return ap_gen

if __name__ == '__main__':

    # 1. 自定义等差序列生成器测试
    ## 1-1: 起始值1,步长为0.5,末项是5(不包含)
    a1 = ArithmeticProgression(1,0.5,5)
    for i in a1:
        """1.0
           1.5
           2.0
           2.5
           3.0
           3.5
           4.0
           4.5
        """
        print(i)


    ## 1-2: 起始值1,步长为Decimal(0.2),末项是2(不包含)
    a2 = ArithmeticProgression(1, Decimal(.2),2)
    for i in a2:
        """
        1
        1.200000000000000011102230246
        1.400000000000000022204460492
        1.600000000000000033306690739
        1.800000000000000044408920985
        """
        print(i)

     #2.利用系统自带的itertools模块生成等差数列
    print('*****2.利用系统自带的itertools模块生成等差数列********')
    import itertools

    ## 2-1 注意:由于该模块方法并没有end参数,如果通过for循环遍历,会生成无穷的序列..直到内存爆掉
    g = itertools.count(1,0.5)
    print(next(g)) # 1
    print(next(g)) # 1.5

    ## 2-2 为了修复上面提到的弊端,可以利用
    # 该方法返回的是一个迭代器,然后接收两个参数:当第二个迭代器中的元素,不满足第一个参数设置的条件时,会终止第二个迭代器迭代
    gen = itertools.takewhile(lambda n: n <3,itertools.count(1,0.5))
    print(list(gen)) # [1, 1.5, 2.0, 2.5]

    # 3.利用itertools.takewhile改造ArithmeticProgression
    print('******3.利用itertools.takewhile改造ArithmeticProgression**********************')
    a3 = ArithmeticProgression2(1, 0.5, 5)
    print(list(a3)) # [1, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]

    a4 = ArithmeticProgression2(1, Decimal(.2), 2)
    print(list(a4)) # [1, Decimal('1.200000000000000011102230246'), Decimal('1.400000000000000022204460492'), Decimal('1.600000000000000033306690738'), Decimal('1.800000000000000044408920984')]

5、yield from

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/27 20:36
# @Author  : Maple
# @File    : 05-yield from.py
# @Software: PyCharm


"""
当生成器函数需要产出另一个生成器 生成的值,传统的解决方案是for循环嵌套

"""

def chain(*iterables):
    for it in iterables:
        for i in it:
            yield i

# 利用yield from
def chain2(*iterables):
    for it in iterables:
        yield from it

if __name__ == '__main__':

    #1.chain方法测试
    s = 'ABC'
    t = tuple(range(3))
    # ['A', 'B', 'C', 0, 1, 2]
    print(list(chain(s,t)))

    #2.chain2方法测试
    s = 'EDF'
    t = tuple(range(3))
    # ['A', 'B', 'C', 0, 1, 2]
    print(list(chain2(s, t))) # ['E', 'D', 'F', 0, 1, 2]

6、可迭代的归约函数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/27 20:45
# @Author  : Maple
# @File    : 06-可迭代的归约函数.py
# @Software: PyCharm


if __name__ == '__main__':

    print(all([1,2,3])) #True
    print(all([1,0,3])) #False
    print(any([1,2,3])) #True
    print(any([1,0,3])) #True
    print(any([0,0.0])) #False
    print(any([])) #False

    g = (n for n in [0,0.0,7,8])
    print(any(g)) #True

7、迭代器哨值

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/27 20:50
# @Author  : Maple
# @File    : 07-迭代器哨值.py
# @Software: PyCharm

"""
迭代器函数iter可以传入两个参数:其中第一个必须是一个可调用对象,第二个就是哨值,当可调用对象返回这个值时,迭代器将停止迭代
"""
from random import randint


def f6():
    # 返回[1-6]区间的整数
    return randint(1,6)

if __name__ == '__main__':
    # print(randint(1,6))

    f6_iter = iter(f6,1)
    for i in f6_iter:
        # 如下结果表明,第三次迭代的值是1,然后迭代终止
        """2
           4
        """
        print(i)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值