【漫漫转码路】Python Day 20

一、可迭代对象

1、可迭代对象和迭代器

可迭代对象:列表、字符串、元组、字典、集合,range等
迭代器:map,zip,filter,reversed,enumerate,

# 例如
from typing import Iterable, Iterator
# from collections.abc import Iterable  #两种方法都可以导入迭代器和迭代对象
print(issubclass(int, Iterable)) # 整数不是可迭代对象,同样浮点数、复数、布尔型都不是,不一一举例
print(issubclass(str, Iterable))  # 字符串是可迭代对象, 是其子类
print(issubclass(list, Iterable))  # 列表是可迭代对象, 是其子类
print(issubclass(set, Iterable))  # 集合也是可迭代对象, 是其子类

print(issubclass(Iterator, Iterable))  # 迭代器也是可迭代对象,是其子类
# 终端显示
False
True
True
True
True

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

2、条件

1、迭代协议:__iter__()
2、序列协议:__getitem__(),且数字从0开始
迭代器需要至少满足一个条件,则为迭代器

# 例如
print('__iter__' in dir(int) or '__getitem__' in dir(int))  # 可见int不满足条件
print('__iter__' in dir(str) or '__getitem__' in dir(str))  # str满足条件 
print('__iter__' in dir(set) or '__getitem__' in dir(set))  # set满足条件
print('__iter__' in dir(Iterator) or '__getitem__' in dir(Iterator))  # 迭代器不满足条件
# 终端显示
False
True
True
True

不再一一列举

3、自定义可迭代对象

# 例如
from collections.abc import Iterable


class Number:
    def __init__(self):
        pass


num = Number()
print(issubclass(Number, Iterable))
# 终端显示
False  # 说明此时Number不是可迭代对象
# 例如
from collections.abc import Iterable


class Number:
    def __init__(self):
        pass

    def __iter__(self):
        pass


num = Number()
print(issubclass(Number, Iterable))
# 终端显示
True  # 增加__iter__之后,变为可迭代对象()
# 例如
from collections.abc import Iterable


class Number:
    def __init__(self):
        pass

    def __getitem__(self,index):
        pass


num = Number()
print(issubclass(Number, Iterable))
for i in num:
    print(i)
# 终端显示
False  # issubclass无法判定__getitem__需要借助for循环来遍历验证
None  # (此处为None无限打印,需要手动停止),可以用for循环遍历则说明是可迭代对象

二、迭代器

1、条件

1、__iter__():
2、__next__():
两个协议一起被称为迭代器协议,只有同时满足两个条件的时候,才是迭代器

# 例如
from collections.abc import Iterable


print('__iter__' in dir(enumerate) and '__next__' in dir(enumerate))
print('__iter__' in dir(zip) and '__next__' in dir(zip))
print('__iter__' in dir(map) and '__next__' in dir(map))
print('__iter__' in dir(reversed) and '__next__' in dir(reversed))
# 终端显示
True
True
True
True
# 可见迭代器都满足这两个条件

注意:
为什么迭代器一定是可迭代对象
因为迭代器必须满足__iter__和__next__两个条件
而可迭代对象需要满足__iter__和__getitem__其中一个条件
也就是说迭代器一定有__iter__,满足迭代对象条件,所以迭代器一定是可迭代对象

2、自定义迭代器

# 例如
class test:
    def __init__(self):
        pass

T1 = test()
print(issubclass(test, Iterator))
# 终端显示
False  # 可见,现在不是迭代器

当加上两个条件之后

# 例如
class test:
    def __init__(self):
        pass
    def __iter__(self):
        pass
    def __next__(self):
        pass

T1 = test()
print(issubclass(test, Iterator))
print(isinstance(T1, Iterator))
# 终端显示
True  # 两个条件都满足之后,变成迭代器
True
# 例如
str1 = 'abcd'
lis1 = list(str1)
print(lis1)  # ['a', 'b', 'c', 'd']

for i in lis1:
    print(i)

用迭代器实现该功能,可迭代对象,迭代协议,__iter__

# 例如
from collections.abc import Iterable, Iterator


class Myiterator:
    def __init__(self, obj):  # 这里的obj接受的是Myclass里面的self
        self.obj = obj  # self.obj就是Myclass里面的self
        self.index = 0  # 设置默认参数需要加self.
        
    def __iter__(self):
        return self  # 如果遍历迭代器,则会调用迭代器的__iter__,而不是可迭代对象的__iter__,同时迭代器的__iter__返回本身,因为本身就是迭代器
        # 迭代器返回本身,同一个迭代器继续进行迭代,所以迭代器只能用一次,
    def __next__(self):
        try:
            res = self.obj.Iterable[self.index]
            self.index += 1
            return res
        except:
            raise StopIteration  # 返回StopIteration被for循环捕获


class Myclass:
    def __init__(self, Iterable):
        *self.Iterable, = Iterable

    def __str__(self):
        # return f'[i for i in self.Iterable]'  # __str__必须返回字符串类型
        return f'{self.Iterable}'

    def __iter__(self):
        return Myiterator(self)  # 可迭代对象不止能使用一次,原因就是在这里,每次返回的迭代器对象不一样


str1 = 'abcd'
test1 = Myclass(str1)
print(test1)


# 以下手动实现一个for循环
test_itr = test1.__iter__()  # 用实例对象手动调用__iter__,因为在执行for循环的时候,程序会自动调用__iter__
# 返回一个迭代器
while True:
    try:
        print(test_itr.__next__())  # 在执行for循环的时候,会执行迭代器的__next__,返回每一个元素,打印输出
    except StopIteration:  # 手动捕获StopIteration
        break

序列协议实现

# 例如
from collections.abc import Iterable, Iterator


class Myclass:
    def __init__(self, Iterable):
        *self.Iterable, = Iterable

    def __str__(self):
        # return f'[i for i in self.Iterable]'  # __str__必须返回字符串类型
        return f'{self.Iterable}'

    def __getitem__(self, index):  #index就是序列,从0开始,可以直接使用,且默认自增1,如果超出序列报错,会被for循环接收,所以还是正常运行
        return self.Iterable[index]


str1 = 'abcd'
test1 = Myclass(str1)

for i in test1:
    print(i)

总结:
迭代器只能使用一次
当可迭代对象有迭代协议和序列协议的时候,优先调用迭代协议

三、生成器

1、生成器与迭代器的关系

Generator,生成器
和导入迭代器的方式一样
生成器是迭代器,但是迭代器不是生成器,因此生成器也是可迭代对象

# 例如
from collections.abc import Iterable, Iterator, Generator  # 导入生成器

print(issubclass(Generator, Iterator))
# 终端显示
True  # 生成器是迭代器的子类

2、生成器执行

有关键字yield,就是生成器函数

# 例如
def func():
    print(1)
    return 2
    print(3)
    return 4
    print(5)


print(func())
# 终端显示
1
2  # 普通函数遇到return之后会结束,然后返回值
# 例如
from collections.abc import Iterable, Iterator, Generator

def func():
    print(1)
    yield 2
    print(3)
    yield 4
    print(5)


res = func()
print(func())
print(func().__next__())
print(func().__next__())
print('--------------------------')
print(res.__next__())
print(res.__next__())
print(res.__next__())
# 终端显示
<generator object func at 0x000002BD5F6245F0>  # 输出func()是一个生成器对象,并不是直接执行
1  # 生成器对象后面 + __next__,可以往下执行
2  # 为什么两次执行都是执行前两行,没有往下执行,因为当用func()调用__next__的时候,func()生成的迭代对象每次都不一样,不是一个迭代对象,所以每次都是从都执行
1
2
--------------------------
1
2
3  # 而res = func(),生成了一个迭代对象,后面始终用这个迭代对象,所以能够一直往下执行
4
5
Traceback (most recent call last):  # 当执行到最后的时候会抛出StopIteration
  File "d:/shenlanclass/PythonFiles/day20/duibi", line 48, in <module>
    print(res.__next__())
StopIteration

注意:
对于生成器,func()并不会直接执行函数体,而需要在调用__next__之后才能往下执行,遇到yield,会返回yield的值,并将程序就此挂起,之后当再一次调用__next__的时候,才会继续往下执行,直到最后,迭代完成,无yield,抛出StopIteration

普通函数不能遍历,而生成器因为是迭代器,可以用for遍历

# 例如
def func():
    print(1)
    yield 2
    print(3)
    yield 4
    print(5)


res = func()
for i in res:   # 执行遍历的时候,会先调用__iter__,然后返回迭代器,再调用__next__,生成器只有用_-next__才能执行,所以这时候程序执行,遇到yield返回值,然后挂起,接下来for循环再次执行__next__,再次执行函数,又一次遇到yield返回,直到最后,抛出,StopIterator,被for循环捕捉
print(i)
# 终端显示
1  # 函数体的执行
2  # yield返回
3  # 函数体的执行
4  # yield返回
5  # 函数体的执行
# 例如
def func():
    print('开始')
    print(res1 := (1, 2))
    res2 = yield res1
    print(res2)
    res3 = yield res1
    print(res3)
    print(res1)

f1 = func()
for i in f1:
    print(i)
# 终端显示
开始
(1, 2)
(1, 2)
None  # res2 = yield res1语句,当yield已经返回res1之后,res2就收不到值了,因此是None
(1, 2)
None
(1, 2)

3、生成器表达式

将列表推导式外面的[]换成()就可以了

# 例如
list1 = [i for i in range(4)]
print(list1)

test1 = (i for i in range(4))  # 生成器表达式
print(test1)
# 终端显示
[0, 1, 2, 3]
<generator object <genexpr> at 0x00000186C6C96AC0>  #生成器对象
# 例如
print(sum((i for i in range(4))))
print('可以简化为')
print(sum(i for i in range(4)))  # 此处可以少一个括号
# 终端显示
6
可以简化为
6

4、迭代器优缺点

优点:迭代器占用内存比较小
因为迭代器是每次取一个出来,用完之后销毁,然后再取一个出来,占用内存始终为1
***迭代器不能索引,***因为迭代器自己都不知道自己多长,他每次只取一个出来,用一个丢一个
不够灵活

四、两个函数

1、next()

是__next__的显示写法;
next(iterator[,default]):要求传一个迭代器,通过调用iterator调用下一个元素,如果迭代完成,则返回default,dafault默认StopIteration

# 例如
def func():
    print(1)
    yield 2
    print(3)
    yield 4
    print(5)


res = func()
print(res.__next__())
print(res.__next__())
print(next(res, '迭代完成'))  # 写成next()形式也能执行
# 终端显示
1
2
3
4
5
迭代完成  # default的参数

2、iter()

iter(object[,sentinel]):
如果没有sentinel,把object转换成迭代器,要求object必须是可迭代对象
如果有sentinel,object必须是可调用的(函数,类,实例对象等),这时会生成一个迭代器,每次调用__next__()会不带参数的调用object,返回调用的结果,如果结果等于sentinel,则触发StopIterator,停止迭代

# 例如
class func():
    def __init__(self, n):
        self.n = n

    def __call__(self):
        self.n += 1
        return self.n

    def __next__(self):  # 有for循环,但是这个__next__不会执行,是因为这个是f1的特殊方法,不是迭代器test1的特殊方法
        exit()


f1 = func(5)
test1 = iter(f1, 14)
for i in test1:  # for在此处调用的是test1的特殊方法__next__(),然后不带参数的调用f1
    print(i)

# print(list(test1))  # 这句可以实现上一句一样的效果
# 终端显示
6
7
8
9
10
11
12
13  # 注意,此处sentinel=14,14不会输出
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值