Python常见面试题012. 可迭代对象和迭代器有啥区别?

python,常见,面试题,迭代,对象,区别 · 浏览次数 : 92

小编点评

**012. 可迭代对象和迭代器有啥区别?** * **可迭代对象**是指可以提供迭代器的任何对象可迭代对象的例子,包括所有序列类型 (例如 list, str 和 tuple)以及某些非序列类型例如 dict, 文件对象以及定义了 __iter__() 方法或是实现了 序列 语义的 __getitem__() 方法的任意自定义类对象。 * **迭代器**则是可迭代对象的一种具体实现,它是一种可以用于逐一返回其成员项的对象可迭代对象的对象。 **两者不是一回事** * 可迭代对象是一种抽象概念,而迭代器是其具体实现。 * 可迭代对象可以是无限的,而迭代器只能表示有限的序列。 * 可迭代对象可以使用 __iter__() 方法提供迭代器,而迭代器本身不需实现 __iter__() 方法。 **总结** * 可迭代对象是一种抽象概念,可以提供任何可迭代器的对象。 * 迭代器是可迭代对象的一种具体实现,它是一种可以用于逐一返回其成员项的对象可迭代对象的对象。

正文

012. 可迭代对象和迭代器有啥区别?

  • 2者不是一回事(废话)

  • 比如

    from collections.abc import Iterable,Iterator
    print(isinstance([1, 2], Iterable))  # True 列表是可迭代对象
    print(isinstance([1, 2], Iterator))  # False 但不是迭代器
    

官方文档

https://docs.python.org/zh-cn/3.10/glossary.html

可迭代对象Iterable

  • 能够逐一返回其成员项的对象。

    简单说,可迭代对象就是能提供迭代器的任意对象
    
  • 可迭代对象的例子包括所有序列类型 (例如 list, strtuple) 以及某些非序列类型例如 dict, 文件对象 以及定义了 __iter__() 方法或是实现了 序列 语义的 __getitem__() 方法的任意自定义类对象。

  • 可迭代对象被可用于 for 循环以及许多其他需要一个序列的地方(zip()map() ...)。

  • 当一个可迭代对象作为参数传给内置函数 iter() 时,它会返回该对象的迭代器。这种迭代器适用于对值集合的一次性遍历。在使用可迭代对象时,你通常不需要调用 iter() 或者自己处理迭代器对象。for 语句会为你自动处理那些操作,创建一个临时的未命名变量用来在循环期间保存迭代器。参见 iteratorsequence 以及 generator


稍作解释

  1. 列表、字符串、元组、字典、集合、文件对象等都是iterable的
from collections.abc import Iterable, Iterator

f = open(r'd:\1.jpg', 'rb')
for _ in ([1, ], (1,), '1', {1: 1}, {1}, f):
    assert isinstance(_, Iterable), f'{_}不是可迭代对象iterable'
else:
    print('都是可迭代对象iterable')
  1. 自定义类

实现_iter_

from collections.abc import Iterable, Iterator

class A:
    def __iter__(self):
        pass  # 实际你肯定不是这样写的

print(isinstance(A(),Iterable))  # True
print(isinstance(A(),Iterator))  # False  注意 , 是Iterable但不是Iterator

实现_getitem_,不符预期

from collections.abc import Iterable, Iterator


class A:
    def __init__(self):
        self.elements = [1, 2, 3]

    def __getitem__(self, i):
        return self.elements[i]


a = A()
print(isinstance(a, Iterable))    # 此处是False的,但下面都是ok的
print(a[0])
for i in a:
    print(i)
# 所以,https://www.liaoxuefeng.com/wiki/1016959663602400/1017323698112640
# 中提到的“凡是可作用于for循环的对象都是Iterable类型”  这句话是不够准确的
# 但多数情况是OK的
  1. 当一个可迭代对象作为参数传给内置函数 iter() 时,它会返回该对象的迭代器
from collections.abc import Iterable, Iterator
list1 = [1,2,3]
it = iter(list1)  # 得到了list对象的迭代器 
print(type(it))  # <class 'list_iterator'>
print(isinstance(it,Iterable))  # True
print(isinstance(it,Iterator))  # True
for _ in it:
    print(_)
s1 = 'abc'
d1 = {1:1}
set1 = {1}
it_s1 = iter(s1)
it_d1 = iter(d1)
it_set1 = iter(set1)
print(type(it_s1))  # str_iterator
print(type(it_d1)) # dict_keyiterator  注意此处,是key的iterator
print(type(it_set1)) # set_iterator

迭代器Iterator

  • 用来表示一连串数据流的对象。
  • 重复调用迭代器的 __next__() 方法(或将其传给内置函数 next())将逐个返回流中的项。当没有数据可用时则将引发 StopIteration 异常。到这时迭代器对象中的数据项已耗尽,继续调用其 __next__() 方法只会再次引发 StopIteration 异常。
  • 迭代器必须具有 __iter__() 方法用来返回该迭代器对象自身,因此迭代器必定也是可迭代对象,可被用于其他可迭代对象适用的大部分场合。一个显著的例外是那些会多次重复访问迭代项的代码。
  • 容器对象(例如 list)在你每次向其传入 iter() 函数或是在 for 循环中使用它时都会产生一个新的迭代器。如果在此情况下你尝试用迭代器则会返回在之前迭代过程中被耗尽的同一迭代器对象,使其看起来就像是一个空容器。
  • 更多信息可查看 迭代器类型
  • CPython 实现细节: CPython 没有统一应用迭代器定义 __iter__() 的要求

稍作解释

  1. 迭代器必定也是可迭代对象

    当前命题的反面
    
  2. 关于next()

    list1 = [1,2,3]
    print(next(list1))  # TypeError: 'list' object is not an iterator
    
    list1 = [1,2,3]
    it_list1 = iter(list1)
    print(next(it_list1)) # 1
    print(next(it_list1)) # 2
    print(next(it_list1)) # 3
    print(next(it_list1)) # StopIteration
    
    list1 = [1,2,3]
    it_list1 = iter(list1)
    print(it_list1.__next__())  # 跟刚才调用next是一样的效果
    # 所以说__next__这种魔术方法的背后往往有一个内置函数(比如len)、运算符(比如>)、操作(比如下标)与之对应
    

其他

  1. for循环的本质:通过iter获取可迭代对象后,不断的在调用next()

    list1 = [1,2,3]
    for _ in list1:
        print(_)
    print('----华丽的分割线-----')
    it_list1 = iter(list1)
    while 1:
        try:
            print(next(it_list1))
        except StopIteration:
            print('到头了')
            break
    
    # for定义
    for_stmt ::=  "for" target_list "in" expression_list ":" suite
                  ["else" ":" suite]
    
    • for 语句用于对序列(例如字符串、元组或列表)或其他可迭代对象中的元素进行迭代:
    • 表达式列表会被求值一次;它应该产生一个可迭代对象。
  2. 为何list不是iterator呢?

    1. 哪里来那么多为什么哦
    2. 你通过代码就可以确定,from collections.abc import Iterable
    3. 非要说,Iterator是惰性的,list是有限的,Iterator可以表示无限数据,比如著名的斐波那契数列

小结

  • 迭代器iterator

    • 迭代器协议是指
    __iter__:返回迭代器对象本身
    __next__:从容器中返回下一项,必须要有它,确保在next()作用下可以得到下一项
    
    • 迭代器iterator一定是可迭代对象
    • iterator一定是有状态的,它要知道我数到哪里了,但却并不需要实现一个容器
  • 可迭代对象iterable

    • 不是迭代器(2者是有区别的!!)

    • 如果一个对象能生成迭代器,那么它就会被称作 iterable

    • for .. in 后面的这个玩意必须是一个iterable(好像跟上面冲突了?其他不然)

    • iterable更像是一个数据的保存者,一个container,它是可以没有状态的,它可以完全不知道你这个iterator数到哪里了,但它需要有能力能产生一个iterator

    • 需要有以下之一

      __iter__:
      __getitem__:需要是个sequence
      
  • 可以有东西既是iterable又是iterator的

    # https://www.bilibili.com/video/BV1ca411t7A9
    # 参考码农高天的示例,稍作更改
    from collections.abc import Iterator, Iterable
    
    
    class Node:
        """
        Iterable
        """
    
        def __init__(self, name):
            self.name = name
            self.next = None
    
        def __iter__(self):
            return NodeIterator(self)
    
    
    class NodeIterator:
    
        def __init__(self, node):
            self.current_node = node
    
        def __next__(self):
            if self.current_node is None:
                raise StopIteration
            node, self.current_node = self.current_node, self.current_node.next
            return node
    
        def __iter__(self):
            return self
    
    
    node1 = Node('node1')
    
    node1_iterator = NodeIterator(node1)
    
    print(isinstance(node1, Iterable))  # T
    print(isinstance(node1_iterator, Iterable)) # T
    print(isinstance(node1, Iterator)) # F
    print(isinstance(node1_iterator, Iterator)) # T
    
    node2 = Node('node2')
    node3 = Node('node3')
    node1.next = node2
    node2.next = node3
    for n in node1:
        print(n.name) # 依次输出node1 node2 node3
    

与Python常见面试题012. 可迭代对象和迭代器有啥区别?相似的内容:

Python常见面试题012. 可迭代对象和迭代器有啥区别?

012. 可迭代对象和迭代器有啥区别? 2者不是一回事(废话) 比如 from collections.abc import Iterable,Iterator print(isinstance([1, 2], Iterable)) # True 列表是可迭代对象 print(isinstance(

Python常见面试题001-005,涉及深浅拷贝、MRO、函数可变参数、作用域、is和==的区别等

Python常见面试题001-005 参考资料 https://github.com/taizilongxu/interview_python https://github.com/hantmac/Python-Interview-Customs-Collection https://github.

Python常见面试题006 类方法、类实例方法、静态方法有何区别?

006. Python中类方法、类实例方法、静态方法有何区别? 全部放一个里面篇幅过大了,就拆分成1个个发布 示例代码 class Human: def __init__(self, name): self.name = name def say(self): print(f'我的名字是{self.

Python常见面试题007. 谈谈Python中__init__和__new__的区别

007. 谈谈Python中__init__和__new__的区别 python中关于dunder method双下方法,或magic method魔术方法的描述多在 https://docs.python.org/zh-cn/3.9/reference/datamodel.html#special

Python常见面试题008. 谈谈python中的解包

008. 谈谈python中的解包 这是个简单的知识点,但有的学员并不理解 unpacking解包 解,对应的是*或者**,也有自动解包之说 包对应的可迭代对象 自动解包 赋值的demo a,b = [1,2] print(a) # 1 print(b) # 2 将容器里面的元素逐个取出来分别赋值

Python常见面试题011. 如何在Python中动态创建类?

011. 如何在Python中动态创建类? 说在前面 答案是type 你印象中的type是用来查看对象的类型的 li = [] type(li) # 得到list 对自定义的类是这样的 class Person: pass wuxianfeng = Person() type(wuxianfeng)

Python常见面试题009. 元组和列表有什么区别

009. 元组和列表有什么区别 这个题是简单的,但要拿满分或者说高分不容易 相同点 | 共性 | 说明 | | | | | 可以存放任意元素 | 一般都放同类型 | | 支持索引访问 | 甚至是负数 | | 支持切片操作 | | | 逗号分隔元素 | | | 都是有序集合(容器) | | | 可以随

Python常见面试题010. Python的int占多大内存?

010. Python的int占多大内存? 示例代码 import sys a = 0 print(a.__sizeof__()) # 24 print(sys.getsizeof(a)) # 24 所以答案是24?并不是!看下面 import sys b = 1 print(b.__sizeof_

Python常见面试题013.请说出下面的代码返回结果是什么?

013.请说出下面的代码返回结果是什么? *的坑;简单题 参考:https://docs.python.org/zh-cn/3.9/library/stdtypes.html#typesseq 示例代码 lists = [[]] * 3 lists[0].append(1) 请问lists此时是什么

Python常见面试题014.请说出下面的代码返回结果是什么?

示例代码 def fun(a, b, c, d): nums = [] for num in range(a, b): nums.append(lambda: num ** c) return nums[d]() print(fun(1, 5, 2, 0)) print(fun(1, 5, 2, 1