Python补遗(一)——可迭代对象,迭代器,生成器

Python补遗(一)——可迭代对象,迭代器,生成器

  • 可迭代对象,迭代器

  • 可迭代对象与迭代器

  1. 可迭代对象是指该对象内部定义了__ iter __ 方法的对象,可迭代对象是Iterable类的实例;
  2. 迭代器是指该对象内部定义了__ iter __ 方法和 __ next __ 方法的对象,迭代器对象是Iterator类的实例;
  3. 当使用for循环遍历某个对象时,for循环底层首先会调用该对象的 __ iter __ 方法生成一个关于该对象的迭代器对象,(如果该对象本身就是迭代器对象则返回自身)然后开始遍历,遍历的本质正是该迭代器调用其内部的 __ next __ 方法的过程;
  4. Python中常用的可迭代对象有list set dict str等;
  5. 迭代器的优点在于当需要某个元素时,才会调用__ next __ 函数对其进行产生,这样子可以节约内存;
  • 代码示例
from collections import Iterable
from collections import Iterator


"""
手工迭代
"""
# aIt是一个可迭代对象
aIt = [1,2,3,4]
print("Is list object a Iterable object?:%s"%isinstance(aIt,Iterable))
print("----------------SPLIT LINE----------------")

# 使用对象内部的__ iter __方法或者Python内置的iter函数都可以生成该对象的迭代器
iterator1 = aIt.__iter__()
print(iterator1)
print(isinstance(iterator1,Iterator))
iterator2 = iter(aIt)
print(iterator2)
print(isinstance(iterator2,Iterator))
print("----------------SPLIT LINE----------------")

# 使用迭代器对象的__ next __方法和Python内置的next函数都可以得到迭代器对象的下一个值
print("iterator1.__next__() is %s"%iterator1.__next__())
print("next(iterator1) is %s"%next(iterator1))
print("iterator2.__next__()  is %s"%iterator2.__next__())
print("next(iterator2) is %s"%next(iterator2))
print("----------------SPLIT LINE----------------")


"""
for 迭代是上述示例的一层封装
"""
for i in aIt:
    print(i)
Is list object a Iterable object?:True
----------------SPLIT LINE----------------
<list_iterator object at 0x00000289E518E5F8>
True
<list_iterator object at 0x00000289E518E668>
True
----------------SPLIT LINE----------------
iterator1.__next__() is 1
next(iterator1) is 2
iterator2.__next__()  is 1
next(iterator2) is 2
----------------SPLIT LINE----------------
1
2
3
4

**注:**但是如果不首先调用__ iter __ 方法或者iter函数将可被迭代对象转化为迭代器,则会产生错误:

next(aIt)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-4-41fa8265cf7e> in <module>()
----> 1 next(aIt)


TypeError: 'list' object is not an iterator
aIt.__next__()
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-39-a31389542b6c> in <module>()
----> 1 aIt.__next__()


AttributeError: 'list' object has no attribute '__next__'

产生错误的原因在于list对象内部并没有实现__ next __ 方法,因此只是一个可迭代对象而非迭代器对象。因此需要使用__ iter __ 方法或者iter函数生成迭代器对象。

hasattr(aIt,"__iter__")
True
hasattr(aIt,"__next__")
False

因此,想要让可迭代对象可以真正进行迭代,只需要让 __ iter __ 方法进行返回一个迭代器即可:

class ListIterator(Iterator):
    
    def __init__(self,list_obj):
        self.list_obj = list_obj
        self.index_num = 0
        
    def __next__(self):
        if self.index_num < len(self.list_obj):
            ret = self.list_obj[self.index_num]
            self.index_num  += 1
            return ret
        else:
            raise StopIteration
            
class Dog(object):
    
    def __init__(self,names):
        self.names = names
    
    def __iter__(self):
        return ListIterator(self.names)

dog = Dog(["Alice","Bob","Cindy"])
for i in dog:
    print(i)
Alice
Bob
Cindy
isinstance(dog,Iterable)
True
isinstance(dog,Iterator)
False
isinstance(iter(dog),Iterator)
True

总结如下:

  • 可迭代对象不一定是迭代器对象;
  • 迭代器对象一定是可迭代对象;
  • 列表,元组,字典,字符串等都是可迭代对象但不是迭代器对象;

参考链接:Python 迭代器(Iterator)

  • 生成器

  1. 生成器对象本身也是一类特殊的迭代器对象,可以使用for循环进行遍历;
  2. 生成器对象只能遍历一次,然后就用尽了,不可重复遍历
  • 代码示例

class ListIterator(Iterator):
    
    def __init__(self,list_obj):
        self.list_obj = list_obj
        self.index_num = 0
        self.isAlreadyDone = list_obj.isAlreadyDone
        
    def __next__(self):
        
        if self.isAlreadyDone == 1:
            raise StopIteration
        else:
            if self.index_num < len(self.list_obj.names):
                ret = self.list_obj.names[self.index_num]
                self.index_num  += 1
                return ret
            else:
                self.list_obj.isAlreadyDone = 1
                raise StopIteration
            
class Dog(object):
    
    def __init__(self,names):
        self.names = names
        self.isAlreadyDone = 0
    
    def __iter__(self):
        return ListIterator(self)

dog = Dog(["Alice","Bob","Cindy"])
for i in dog:
    print(i)
Alice
Bob
Cindy
# 将返回空
for i in dog:
    print(i)

上述代码示例中的dog对象正是一个生成器对象。由于for循环对其进行迭代时,每次都会调用dog对象的 __ iter __ 方法,但却不会重新调用dog的__ init __ 方法对其进行重新创建,因此,只需要在dog中加入一个判断是否已经完成首次迭代的属性,并且在 __ iter __ 方法中将dog对象传入迭代器中,那么迭代器对象不管被建立几次,其中的判断属性isAlreadyDone则不会受到影响。

在Python中,生成器大致可以分成两类,生成器函数以及生成器解析式:

  • 第一类生成器:生成器函数
"""
此时,像这种带有yield的“函数”,其本质上是一个可以随时挂起的生成器,就不再是函数了。
我们可以像使用迭代器那样来使用这种生成器函数,其运行过程就是:每当执行到yield语句则返回一次迭代的结果,
然后内部挂起,当下次next的调用时继续从下次挂起的位置执行,直到yield。如此循环往复。
"""
def g():
    for i in range(10):
        yield 2*i
        
g = g()
for i in g:
    print(i)
0
2
4
6
8
10
12
14
16
18
  • 第二类生成器:生成器解析式
"""
生成器解析器的语法和列表解析式几乎一致,但是用( )进行开始和结尾,表示返回的是一个生成器,
这里要注意,此种语法并非元组解析式
"""
g = (i*2 for i in range(10))
g
<generator object <genexpr> at 0x0000027FA2AAAE58>
g.__next__()
0
next(g)
2
for i in g:
    print(i)
4
6
8
10
12
14
16
18
for i in g:
    print(i)

总结如下:

  • 生成器是一类特殊的迭代器,但是每个生成器对象只能被遍历一轮,然后用尽;
  • 生成器和迭代器一样,好处就是可以节约内存,而不是一开始就在内存生成全部的数据
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值