【疑问解答】魔法方法__iter__和__next__、__getitem__方法及其相关概念

一、for … in的机制

参考
Python 中的 for 循环与可迭代对象介绍
迭代:一个可迭代对象是不能独立进行迭代的,Python中,迭代是通过for … in来完成的。凡是可迭代对象都可以直接用for… in…循环访问(for…in…只支持可迭代对象),这个语句其实做了两件事:第一件事是调用__iter__()获得一个可迭代器,第二件事是循环调用__next__()。
其实在执行 for 循环时,实际执行流程是这样的:
-> for i in a 相当于执行 iter(a)
-> 每次迭代时会执行一次 __next__ 方法,返回一个值
-> 如果没有可迭代的数据,抛出 StopIteration 异常,for 会停止迭代

a = [1, 2, 3]
for i in a:
    do_something()

其实在python内部进行了类似如下的转换:

a = [1, 2, 3]
for i in iter(a):
    do_something()

ps:for循环可以用while来替代,手动catch StopIteration:

iter_ = iter([1,2,3,4,1,2,3,4,"Done"])  # 将其变为迭代器本身
while True:
    try:
        print(next(iter_))
    except StopIteration:
        del iter_  # python3 中,循环内部变量的迭代器在循环完成后会被废除,Python2 不需要
        break

注:当调用iter()函数的时候,生成了一个迭代器对象,要求__iter__必须返回一个实现了__next__的迭代器对象,我们就可以通过next()函数访问这个对象的下一个元素了,并且在你不想继续有迭代的情况下抛出一个StopIteration的异常(for语句会捕获这个异常,并且自动结束for)。
iter() 函数用来生成迭代器
next() 返回迭代器的下一个项目

总结

  • for…in…的右边只支持可迭代对象。
  • for…in…可以捕获StopIteration的异常,并且自动结束for。

二、迭代器和可迭代对象、生成器

参考
Python进阶——迭代器和可迭代对象有什么区别?生成器。
【Python魔术方法】迭代器(iter__和__next)
Python基础9——可迭代对象、迭代器、生成器
【Python魔术方法】生成器(yield表达式)
总结

  1. 迭代器协议:一个对象要想使用 for 的方式迭代出容器内的所有数据,这就需要这个类实现「迭代器协议」。
  2. 迭代器(iterator):一个类如果实现了「迭代器协议」,就可以称之为「迭代器」。在 Python 中,实现迭代器协议就是要在类中实现以下 2 个方法:
    __iter_:这个方法返回对象本身,即 self。
    __next
    _:这个方法每次返回迭代的值,在没有可迭代元素时,抛出 StopIteration 异常。
  3. 可迭代对象( iterable):使如果只是实现了 __iter__(没有实现__next__),并且这个方法返回的是一个迭代器对象,那么这个类的实例就只是一个可迭代对象,因为它的迭代细节是交给了另一个类来处理。或实现了__getitem__,其实例化对象可以从0开始索引,例object[i]。
    4.迭代区别: 可迭代对象可以一直重复迭代,因为它__iter__方法返回一个迭代器;而迭代器对象只能迭代一次。迭代器对象经过一次for后不能再次for,可迭代对象经过一次for,还可以重新for。
    5.__iter__若返回的是自身,则是迭代器;若返回的是某个实现了迭代器的类的对象,则是可迭代对象。
    6.可迭代对象可以用内置iter函数可以获取迭代器的对象,例如list、tuple、set、dict 类型。
class A:
    # A是迭代器 因为它实现了 __iter__ 和__next__方法
    def __init__(self, n):
        self.idx = 0
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return val
        else:
            raise StopIteration()  # 抛出for .. in 会捕获这个错误,并停止for .. in

class B:
    # B不是迭代器 但B的实例是一个可迭代对象
    # 因为它只实现了 __iter__
    # __iter__返回了A的实例 迭代细节交给了A
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return A(self.n)

# a是一个迭代器 同时也是一个可迭代对象
a = A(3)
for i in a:  # 输出0-2
    print(i)

print(iter(a)) # <__main__.A object at 0x10eb95550>

for i in a:  # 不会输出任何东西,迭代器只能迭代一次。
    print(i)

# b不是迭代器 但它是可迭代对象 因为它把迭代细节交给了A
b = B(3)
for i in b:  # # 输出0-2(可迭代对象可以一直重复迭代)
    print(i)
# <__main__.A object at 0x10eb95450>
print(iter(b))

for i in b:  # 输出0-2(可迭代对象可以一直重复迭代)
    print(i)

python 异常处理 StopIteration

python 异常处理 StopIteration

如何判断一个对象是可迭代对象或者迭代器对象?

可以使用collection.abs里面的Iterator和Iterable配合isinstance函数来判断一个对象是否是可迭代的,是否是迭代器对象。

from collections.abc import Iterable, Iterator
print(isinstance(object_name, Iterable))  # 返回True表明是可迭代对象
print(isinstance(object_name, Iterator))  # 返回True表明是迭代器对象

三、__getitem__方法

__getitem__(sekf, index):拦截索引运算符,当出现“实例名[index]”的实现时,Python会调用这个实例定义或者继承的__getitem__方法(如果有的话)。
__getitem__的实现有两种

  • __getitem__把实例变成可迭代器(实际对象并不是可迭代对象或者迭代器对象)
  • 使用下标获取类中对应元素值

__getitem__把实例变成可迭代对象(实际并不是)

参考:__getitem__把实例变成可迭代器

from collections.abc import Iterable, Iterator


class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __getitem__(self, item):
        print('uses getitem')  # 判断是否使用getitem
        return self.employee[item]

company = Company(["tom", "bob", "jane"])
for em in company:
    print(em)

for em in company:
    print(em)

print(isinstance(company, Iterable))  # 返回True表明是可迭代对象
print(isinstance(company, Iterator))  # 返回True表明是迭代器对象

输出:

uses getitem
tom
uses getitem
bob
uses getitem
jane
uses getitem

uses getitem
tom
uses getitem
bob
uses getitem
jane
uses getitem

False
False

:__getitem__在for中会多循环一次。

__getitem__使用下标获取类中对应元素值

参考:class类中__getitem__的作用及其使用方法

class Test():
    def __init__(self):
        self.a=[1,2,3,4,5]
    def __getitem__(self,idx):
        return(self.a[idx])
data=Test()
print(data)
print(data[0])

输出:

<__main__.Test object at 0x000002BBB3256DF0>
1

四、__iter__和__getitem__区别

参考:【Python】__iter__和__getitem__区别
参考:Python __getitem__和自定义的迭代器

  • __iter__方法的作用是让对象可以用for … in循环遍历
  • __getitem__方法是让对象可以通过“实例名[index]”的方式访问实例中的元素,也可让对象可以用for … in循环遍历。
  • 当两者同时存在时,实例名[index]直接进入__getitem__()函数,for则直接进入的是__iter__函数,也就意味着__iter__是优先读取的。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值