Python 可迭代对象、迭代器、生成器的详解

Python 可迭代对象、迭代器、生成器的详解

一、判断可迭代对象、迭代器:

使用模块from collections import Iterable, Iterator,可以检测该对象是可迭代对象还是迭代器

Iterable: 检查是否可迭代对象
Iterator:检查是否迭代器

举个例子,判断可迭代对象(Iterable):

from collections import Iterable, Iterator  

>>> from collections import Iterable, Iterator
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> my_tuple = (1, 2, 3)
>>> isinstance(my_tuple, Iterable)
True
>>> my_dict = {"name": "fe_cow"}
>>> isinstance(my_dict, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(66)), Iterable)  # 注意这是生成器
True
>>> isinstance(666, Iterable)
False
  • 以上结论:可以看出列表、集合、元组、字典、字符串、生成器都是可迭代对象。

举个栗子,判断迭代器(Iterator):

>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance(100, Iterator)
False
>>> my_tuple = (1, 2, 3)
>>> isinstance(my_tuple, Iterator)
False
>>> my_dict = {"name": "fe_cow"}
>>> isinstance(my_dict, Iterator)
False
>>> isinstance(666, Iterator)
False
  • 以上结论:除了生成器是迭代器外,其余的类型都不是迭代器,因为生成器内部调用了next()函数

总结

1.for 循环的对象都是可迭代类型(Iterable)

2.可用于next()函数的对象都是迭代器(Iterator ),它表示一个惰性计算的序列

3.列表、集合、元组、字典、字符串都是可迭代对象(Iterable),但不是迭代器(Iterator),可以通过iter()函数变成迭代器(Iterator)对象

  • iter()函数将可迭代对象,变成迭代器对象:

    >>> isinstance(iter("abc"), Iterator)
    True
    

迭代器一定是可迭代对象,但是可迭代对象不一定是迭代器

那么下面来看看可迭代对象、迭代器、生成器的详解吧!

二、可迭代对象(Iterable):

可迭代对象是指使用iter()内置函数可以获取迭代器的对象

详细的讲

1.检查对象是否实现了__ iter __ () 方法,如果实现该方法调用它,会获取一个可迭代对象

2.如果没有实现__ iter __ () 方法,但是实现了__ getitem __ (index)方法,按顺序(从索引0开始)获取元素,参数idenx是从0开始的整数,Python会创建一个迭代器

3.若该对象获取失败,Python会抛出TypeError异常,通常会提示xxxxxx object is not iterable

1.__ iter __ 方法:

class Test(object):

    def __init__(self, name, my_list=[]):
        self.name = name
        self.my_list = my_list

    def __iter__(self):
        print('__执行iter__方法')
        pass

test = Test('fe_cow', [1, 2, 3, 4, 5])
print(test)
print(isinstance(test, Iterable))

# 输出结果如下:
<__main__.Test object at 0x000001C4F2B1B940>
True 
  • 总结:可以看出一个对象拥有 __ iter __ 方法,该对象是一个可迭代对象

2.__ getitem __ 方法:

class Test(object):

    def __init__(self, name, my_list=[]):
        self.name = name
        self.my_list = my_list

    def __getitem__(self, item):
        print('__执行getitem__方法')
        return self.my_list[item]


test = Test('fe_cow', [1, 2])
print(test)  # 输出结果:<__main__.Test object at 0x000002CDAD7EBA20>

print(isinstance(test, Iterator))  # False

for item in test:
    print(item)

# 输出结果如下:
__执行getitem__方法
1
__执行getitem__方法
2
  • 总结:可以看出在没有 __ iter __ 方法时,会去调用 __ getitem __ 方法,返回的是一个迭代器,通过for 循环可以遍历该对象的返回值。
  • 注意:为什么它是迭代器,而使用isinstance(test, Iterator)返回的是False呢?
    • 该方式会自动忽略 __ getitem __ 函数,仅当对象实现了 __ iter __ 函数才返回 True,其他一律返回 False

3.抛出异常:

class Test(object):

    def __init__(self, name, my_list=[]):
        self.name = name
        self.my_list = my_list

test = Test('fe_cow', [1, 2, 3, 4, 5])
print(test)  # 输出结果:<__main__.Test object at 0x0000011B07C3B940>

for item in test:
    print(item)

# 报错信息:
Traceback (most recent call last):
  File "C:/Users/lh9/PycharmProjects/request/Python魔法函数.py", line 581, in <module>
    for item in test:
TypeError: 'Test' object is not iterable
  • 总结:若该对象,没有实现以上两种方法,当你把它当作可迭代对象处理时,就会抛出以上错误信息。

三、迭代器(Iterator):

迭代器就是实现__ next __ ()方法,返回序列中的下一个元素,若该元素没有了,就抛出StopIteration异常。

1.使用场景:

1.for 循环

2.扩展集合类型

3.列表推导、字典推导、集合推导

4.元组拆包

5.调用函数,使用*拆包实参等

2.特点:

可迭代:迭代器内部实现了__ iter __ 方法,所以它可迭代。

易损耗:迭代器经过一次逐次取值的循环后便耗尽了,若想再次迭代须重建迭代器。

模式:按需要一次获取一个数据。

3.斐波那契数列:

使用斐波那契数列,来讲解迭代器再好不过了,举个使用栗子:

from collections import Iterable, Iterator


class Fib(object):
    """迭代器经典栗子:
        使用斐波那契数列:
         0, 1, 1, 2, 3, 52位数相加等于第3位数
    """

    def __init__(self, num):
        self.fib_nums = num
        self.a = 0
        self.b = 1
        self.current_num = 0

    def __iter__(self):
        """实现可迭代方法, 返回迭代器本身"""
        return self

    def __next__(self):
        """实现next方法,返回下一个可用元素"""
        if self.current_num < self.fib_nums:
            result = self.a
            self.a, self.b = self.b, self.a + self.b
            self.current_num += 1
            return result
        else:
            raise StopIteration

fib = Fib(6)

print(isinstance(fib, Iterator))  # 输出结果:True,可以判定该fib对象是一个迭代器

for item in fib:
    print(item)

# 输出结果如下:
0
1
1
2
3
5
  • 总结:如果一个对象拥有__ next __ ()方法,该对象是迭代器

4.for 循环本质:

for 循环本质就是通过一个可迭代对象,不断调用__ next __ 方法实现

举个栗子:

my_list = [1, 2, 3, 4, 5, 6]
for item in my_list:
    pass

等价于:

# 首先获得Iterator对象:
iterator_obj = iter([1, 2, 3, 4, 5, 6])
# 循环:
while True:
    try:
        # 获得下一个值:
        result = next(iterator_obj)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

5.总结:

迭代器一定拥有 __ iter __ 方法、__ next __ 方法,而可迭代对象一定拥有 __ iter __ 方法,但不能实现 __ next __ 方法,这就是上面所说的,迭代器一定是可迭代对象,但是可迭代对象不一定是迭代器

四、生成器(Generator ):

生成器也是函数,函数中只要有yield关键字,那么它就是生成器函数,返回值为生成器

生成器存在 __ inter __ 、 __ next __ 两种方法(不需要手动实现两种方法),因此它是一个迭代器

1.生成器概念介绍:

生成器函数:拥有yield关键字的Python函数。

生成器表达式:制造生成器的工厂,支持惰性产值。

生成器工厂函数:返回生成器的函数,定义体中可以没有yield关键字。

2.为什么使用生成器?

为什么列表代替不了生成器?因为列表容量肯定有限的,如创建一个包含666万个元素列表,不仅占用很大的存储空间,若我们仅仅需要访问前面几个元素,那后面绝大数元素占用空间都白浪费,在Python中这种一边循环一边计算的机制,称为生成器(Generator),生成器一次只能产生一个值,这样消耗的内存数量将大大减少

3.生成器函数:

只要Python函数的定义体中有yield关键字,该函数就是生成器函数,调用生成器函数时,会返回一个生成器对象

简单理解:生成器函数是生成器工厂,普通函数与生成器函数区别在于定义体中yield关键字

def func():
    for item in range(6):
        yield item

print(func())  # 输出结果:<generator object func at 0x000001BB1D7F26D0>  可以看出它是一个生成器

f = func()  # 接收这个生成器

# 对生成器进行操作
print(next(f))  # 输出结果:0
print(next(f))  # 输出结果:1
print(next(f))  # 输出结果:2

看看它的执行流程:

def func():
    print('__a__')
    yield 1
    print('__b__')
    yield 2
    print('__c__')
    return 3

f = func()
print(f) # 输出结果:<generator object func at 0x00000293005D26D0>
# 可以看出是一个生成器,证明函数的return 并没有生效

print(next(f))  # 输出结果:__a__  1    说明执行第一个yield
print(next(f))  # 输出结果:__b__  2    说明执行第二个yield
print(next(f))  # 输出结果:抛出异常:StopIteration: 3
# 第二个 yield之后执行,当没有更多yield之后,抛出异常,正好是函数的返回值

总结

1.生成器函数执行时,并不会执行函数体

2.当next生成器时,会从当前代码执行到之后的第一个yield,会弹出值并暂停函数

3.当再次执行next生成器时,从上次暂停处开始向下执行

4.当没有多余yield时,会抛出StopIteration异常,异常的值是函数的返回值

4.生成器表达式:

生成器表达式,将列表推导中的[ ]替换为( ),让代码表的更整洁

生成器表达式可理解为列表推导的惰性版本,不会迫切构建列表,而是返回一个生成器,按惰性生成元素。

使用栗子:

# 列表推导式
my_list = [item for item in range(6)]
print(type(my_list))  # 输出结果:<class 'list'>
print(my_list)  # 输出结果:[0, 1, 2, 3, 4, 5]

# 生成器表达式
my_generator = (item for item in range(10))
print(type(my_generator))  # 输出结果:<class 'generator'>
print(my_generator)  # 输出结果:<generator object <genexpr> at 0x0000023DE0FD26D0>

5.yield关键字:

函数中使用yield关键字,这个函数就变成了生成器函数,yield只能定义在函数中,当我们调用这个函数时,函数内部代码并不会立即执行,这个函数会返回一个生成器对象,当我们使用for对其进行迭代时,函数内的代码才会被执行。

yield与return区别

1.return时,这个函数的局部变量都被销毁,所以的return都是已经得到了结果将其返回。

2.yield是产生一个可以恢复的生成器函数,恢复了局部变量,生成器只有在调用next()方法时,才运行函数生成一个结果。

3.yield会记住函数执行的位置,下次再执行的时会从上次的位置继续向下执行;若再函数中使用return,函数就直接退出,无法继续执行。

6.总结:

1.具有yield关键字的函数都是生成器

2.yield可以理解为return,返回的值给调用者,不同return返回后,函数会被释放,而生成器不会

3.直接调用next方法或for语句j进行一下次迭代时,生成器会从yield下一句开始执行,遇到下一个yield为止,若没有yield 会抛出StopIteration

  • 15
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值