python 一篇文章读懂可迭代对象、迭代器、生成器

1、迭代

1、什么是迭代

Python中的迭代指的是按照某种顺序逐个访问容器(如列表、元组、字典等)中的元素的过程。Python中的迭代可以通过for循环、while循环和生成器实现。

2、迭代的方式

        三种方式:

                for循环迭代

# 循环遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
  print(fruit)
 
# 循环遍历字符串
string = "Hello, World!"
for char in string:
  print(char)
 
# 循环遍历字典
dict = {"name": "Tom", "age": 18, "gender": "male"}
for key in dict:
  print(key, dict[key])

                while循环迭代

i = 0
while i < 5:
  print(i)
  i += 1

                生成器的迭代

# 生成器函数
def my_range(start, end, step):
  while start < end:
    yield start
    start += step
 
# 使用生成器
for i in my_range(0, 10, 2):
  print(i)

生成器实现的range

2、可迭代对象

1、可迭代对象

凡是实现了__iter__ (或者__getitem__)方法的都是可迭代对象(Iterable),可迭代对象都可以被迭代器(Iterator)进行迭代操作。

python中基础序列基本都是可迭代对象,我们可以用内置函数 isinstance() 来进行判断。

from collections.abc import Iterable, Iterator

#python的可迭代对象
ls = [1, 2, 3, 4, 5]
tu = ('a', 'b')
dic = {'a': 1, 'b': 2}
strs = 'abcd'
se = {'d', 2, 3}

print("列表是不是可迭代对象:", isinstance(ls, Iterable))
print("元组是不是可迭代对象:", isinstance(tu, Iterable))
print("字典是不是可迭代对象:", isinstance(dic, Iterable))
print("字符串是不是可迭代对象:", isinstance(strs, Iterable))
print("集合是不是可迭代对象:", isinstance(se, Iterable))
print('分割线'.center(50, '='))
print(ls.__dir__())
print(tu.__dir__())
print(dic.__dir__())
print(strs.__dir__())
print(se.__dir__())


列表是不是可迭代对象: True
元组是不是可迭代对象: True
字典是不是可迭代对象: True
字符串是不是可迭代对象: True
集合是不是可迭代对象: True
=======================分割线========================
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__iadd__', '__imul__', '__new__', '__reversed__', '__sizeof__', 'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse', 'sort', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__len__', '__getitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__new__', '__getnewargs__', 'index', 'count', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__init__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__or__', '__ror__', '__ior__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__contains__', '__new__', '__sizeof__', 'get', 'setdefault', 'pop', 'popitem', 'keys', 'items', 'values', 'update', 'fromkeys', 'clear', 'copy', '__reversed__', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
['__repr__', '__hash__', '__str__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__mod__', '__rmod__', '__len__', '__getitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__new__', 'encode', 'replace', 'split', 'rsplit', 'join', 'capitalize', 'casefold', 'title', 'center', 'count', 'expandtabs', 'find', 'partition', 'index', 'ljust', 'lower', 'lstrip', 'rfind', 'rindex', 'rjust', 'rstrip', 'rpartition', 'splitlines', 'strip', 'swapcase', 'translate', 'upper', 'startswith', 'endswith', 'removeprefix', 'removesuffix', 'isascii', 'islower', 'isupper', 'istitle', 'isspace', 'isdecimal', 'isdigit', 'isnumeric', 'isalpha', 'isalnum', 'isidentifier', 'isprintable', 'zfill', 'format', 'format_map', '__format__', 'maketrans', '__sizeof__', '__getnewargs__', '__doc__', '__setattr__', '__delattr__', '__init__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__dir__', '__class__']
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__sub__', '__rsub__', '__and__', '__rand__', '__xor__', '__rxor__', '__or__', '__ror__', '__isub__', '__iand__', '__ixor__', '__ior__', '__len__', '__contains__', '__new__', 'add', 'clear', 'copy', 'discard', 'difference', 'difference_update', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', '__reduce__', 'remove', '__sizeof__', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']


2、可迭代对象如何被迭代
  • 当对可迭代对象进行迭代(for)操作时,首先会调用内置函数iter()返回一个迭代器(Iterator)
  • 对返回的迭代器调用 next()函数,逐个读取迭代器内容
# 迭代器对象
class ItRange():
    def __init__(self,num):
        self.num = num
        self.counter = -1     # 注意,counter是从-1开始的,所以第一次调用__next__函数,会返回0

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == self.num:   # 如果迭代次数超过num,则触发异常StopIteration,停止迭代,并返回属性counter
            raise StopIteration()
        return self.counter

# 可迭代对象
class XRange():
    def __init__(self,max_num):
        self.max_num = max_num

    def __iter__(self):
        return ItRange(self.max_num)

print(list(XRange(10)))  # 你可以看到输出的最终结果就是[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


obj1 = XRange(10)

for item in obj1:   
# 在此时,首先是调用了XRange的__iter__方法,返回了一个迭代器,
# 然后逐次调用迭代器的__next__方法,形成每一个值
    print(item)

3、迭代器

1、迭代器协议

一个对象需要提供next方法,该方法要么返回迭代对象中的下一项,要么就抛StopIteration异常,迭代终止。

2、迭代器的实现原理

在python中,只要一个对象实现了以下两个方法,那么它就是一个迭代器
iter _: 返回一个可迭代对象
next _: 返回迭代器的下一项,当没有元素可以返回时抛StopIteration异常,迭代终止。

class ItRange():
    def __init__(self,num):
        self.num = num
        self.counter = -1     # 注意,counter是从-1开始的,所以第一次调用__next__函数,会返回0

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == self.num:   # 如果迭代次数超过num,则触发异常StopIteration,停止迭代,并返回属性counter
            raise StopIteration()
        return self.counter
3、迭代器的实现(斐波那契)
from collections.abc import Iterable, Iterator

#实现一个返回斐契那波数列的迭代器
class fibo_iterator():

    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        # 由于自己就是一个迭代器,所以返回自己就行
        return self

    def __next__(self):

        if self.b > 100:
            raise StopIteration
        else:
            self.a, self.b = self.b, self.a + self.b
            return self.a
            

4、生成器

生成器(generator)也是一种迭代器,在每次迭代时返回一个值,直到抛出  StopIteration异常

1、生成器表达式
a = (x ** 2 for x in range(100))
print(type(a))

E:\env\python39\python.exe D:\项目\python\deke\test.py 
<class 'generator'>

生成器也可以迭代


2、生成器函数

含有 yield 关键字的函数,调用该函数时会返回一个生成器

# 基于生成器和可迭代对象构建range函数
class genRange():
    def __init__(self,num):
        self.num = num
    
    def __iter__(self):
        counter = 0
        while counter < self.num:
            yield counter
            counter += 1

obj1 = genRange(5)
for item in obj1:
    print(item)
3、生成器进阶

生成器的方法:

生成器比生成器多了三个方法:close()send() throw()

关闭生成器 close()

g=(i for i in range(5))
print(g.__next__())
print(g.__next__())
print(g.__next__())
g.close()
print(g.__next__())

生成3个元素后,生成器将不在生成对象了
报:StopIteration

send():和next一样可以用来生成数据,可以往生成器内部传递数据(可以和生成器内部进行交互)

使用生成器时的注意:
1、不管使用next方法还是send方法,每次执行到yield时,都会暂停,并且将yield后面的值返回出来
2、send使用时,生成器内部必须处于yield暂停的位置(使用send之前,生成器至少是通过next去生成一次数据)

案例一:

def demo():
    for i in range(10):
        yield i


g = demo()
print('send生成的数据:', g.send(6))

TypeError: can't send non-None value to a just-started generator

# 使用send之前没有通过next去生成一次数据

案例二:

def demo():
    for i in range(10):
        res = yield i
        print('send传入的数据,', res)


g = demo()
print('next生成的数据:', next(g))
print('send生成的数据:', g.send(6))



E:\env\python39\python.exe D:\项目\python\deke\test.py 
next生成的数据: 0
send传入的数据, 6
send生成的数据: 1

案例三:

def work():
    host = yield  # todo 2、代码暂停到这里
    for i in range(5):
        host = yield "https://{}/user/login".format(host)


g = work()
next(g)  # todo 1、启动生成器

print('send方法生成的数据:', g.send("www.baidu.com"))
# todo 3、send方法传值,会把值传到host     4、执行for循环把host传到yield中,并且返回
# todo 5、到行代码就停下来了
print('send方法生成的数据:', g.send("www.baidu1.com"))
print('send方法生成的数据:', g.send("www.baidu2.com"))
print('send方法生成的数据:', g.send("www.baidu3.com"))
print('send方法生成的数据:', g.send("www.baidu4.com"))


send方法生成的数据: https://www.baidu.com/user/login
send方法生成的数据: https://www.baidu1.com/user/login
send方法生成的数据: https://www.baidu2.com/user/login
send方法生成的数据: https://www.baidu3.com/user/login
send方法生成的数据: https://www.baidu4.com/user/login

throw()

在生成器中抛出异常,效果等同于raise

5、生成器和迭代器的区别

1、实现方式不同

迭代器是通过实现__iter__和__next__方法来实现的。__iter__方法返回迭代器对象本身,__next__方法返回下一个值。当没有更多的元素时,__next__方法会引发StopIteration异常。

生成器则是一种特殊的迭代器,它是通过yield关键字来实现的。yield关键字会暂停函数执行,并返回一个值,下次调用时会从上次暂停的位置继续执行,直到函数执行结束或遇到return语句。

2、调用方式不同

迭代器可以使用for循环或者next方法进行迭代操作。

生成器可以使用for循环或者next方法进行迭代操作,也可以使用yield from关键字来迭代嵌套的生成器。

3、内存占用不同

迭代器需要将所有的元素保存在内存中,如果元素非常多的话,会占用大量的内存空间。而生成器则可以一次生成一个元素,并在下次迭代时再生成下一个元素,因此不需要一次性将所有元素都保存在内存中,可以大大节省内存空间。

4、可复用性不同

迭代器只能遍历一次,遍历结束后就不能再次使用。而生成器则可以被多次遍历,每次遍历时会重新生成一组值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值