Python迭代器的实现原理

作者:??编程学习网???? 时间:2022-12-13 09:26:22 

前言:

在Python里面,只要类型对象实现了__iter__,那么它的实例对象就被称为可迭代对象(Iterable),比如字符串、元组、列表、字典、集合等等。而整数、浮点数,由于其类型对象没有实现__iter__,所以它们不是可迭代对象。

from typing import Iterable
print(
   isinstance("", Iterable),
   isinstance((), Iterable),
   isinstance([], Iterable),
   isinstance({}, Iterable),
   isinstance(set(), Iterable),
)  # True True True True True

print(
   isinstance(0, Iterable),
   isinstance(0.0, Iterable),
)  # False False

可迭代对象的一大特点就是它可以使用for循环进行遍历,但是能被for循环遍历的则不一定是可迭代对象。

我们举个栗子:

class A:
   def __getitem__(self, item):
       return f"参数item: {item}"
a = A()
#内部定义了 __getitem__
#首先可以让实例对象像字典一样访问属性
print(a["name"])  # 参数item: name
print(a["satori"])  # 参数item: satori

# 此外还可以像可迭代对象一样被for循环
# 循环的时候会自动给item传值,0 1 2 3...
# 如果内部出现了StopIteration,循环结束
# 否则会一直循环下去。这里我们手动break
for idx, val in enumerate(a):
   print(val)
   if idx == 5:
       break
"""
参数item: 0
参数item: 1
参数item: 2
参数item: 3
参数item: 4
参数item: 5
"""

所以实现了__getitem__的类的实例,也是可以被for循环的,但它并不是可迭代对象。

from typing import Iterable
print(isinstance(a, Iterable))  # False

打印的结果是 False。

总之判断一个对象是否是可迭代对象,就看它的类型对象有没有实现__iter__。可迭代对象我们知道了,那什么是迭代器呢?很简单,调用可迭代对象的__iter__方法,得到的就是迭代器。

迭代器的创建

不同类型的对象,都有自己的迭代器,举个栗子:

lst = [1, 2, 3]
#底层调用的其实是list.__iter__(lst)
#或者说PyList_Type.tp_iter(lst)
it = lst.__iter__()
print(it)  # <list_iterator object at 0x000001DC6E898640>
print(
   str.__iter__("")
)  # <str_iterator object at 0x000001DC911B8070>
print(
   tuple.__iter__(())
)  # <tuple_iterator object at 0x000001DC911B8070>

迭代器也是可迭代对象,只不过迭代器内部的__iter__返回的还是它本身。当然啦,在创建迭代器的时候,我们更常用内置函数iter。

lst = [1, 2, 3]
# 等价于 type(lst).__iter__(lst)
it = iter(lst)

但是iter函数还有一个鲜为人知的用法,我们来看一下:

val = 0
def foo():
   global val
   val += 1
   return val
# iter可以接收一个参数: iter(可迭代对象)
# iter也可以接收两个参数: iter(可调用对象, value)
for i in iter(foo, 5):
   print(i)
"""
1
2
3
4
"""

进行迭代的时候,会不停地调用接收的可调用对象,直到返回值等于传递第二个参数value,在底层被称为哨兵,然后终止迭代。

我们看一下iter函数的底层实现:

static PyObject *
builtin_iter(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
   PyObject *v;

// iter函数要么接收一个参数, 要么接收两个参数
   if (!_PyArg_CheckPositional("iter", nargs, 1, 2))
       return NULL;
   v = args[0];
   //如果接收一个参数
   //那么直接使用 PyObject_GetIter 获取对应的迭代器即可
   //可迭代对象的类型不同,那么得到的迭代器也不同
   if (nargs == 1)
       return PyObject_GetIter(v);
   // 如果接收的不是一个参数, 那么一定是两个参数
   // 如果是两个参数, 那么第一个参数一定是可调用对象
   if (!PyCallable_Check(v)) {
       PyErr_SetString(PyExc_TypeError,
                       "iter(v, w): v must be callable");
       return NULL;
   }
   // 获取value(哨兵)
   PyObject *sentinel = args[1];
   //调用PyCallIter_New
   //得到一个可调用的迭代器, calliterobject 对象
   /*
   位于 Objects/iterobject.c 中
   typedef struct {
       PyObject_HEAD
       PyObject *it_callable;
       PyObject *it_sentinel;
 } calliterobject;
   */
   return PyCallIter_New(v, sentinel);
}

来源:https://juejin.cn/post/7091845218299248647

标签:Python,迭代器,原理
0
投稿

猜你喜欢

  • ES6 let和const定义变量与常量的应用实例分析

    2024-05-28 15:41:17
  • Python使用回溯法子集树模板获取最长公共子序列(LCS)的方法

    2021-04-14 04:55:49
  • javascript实现鼠标选取拖动或Ctrl选取拖动

    2021-08-21 19:08:33
  • 用Python实现QQ游戏大家来找茬辅助工具

    2021-09-10 16:28:44
  • Firefox下无法正常显示年份的解决方法

    2024-04-18 09:39:50
  • Array.prototype.slice

    2010-05-07 12:43:00
  • 关于python的bottle框架跨域请求报错问题的处理方法

    2021-12-06 23:00:11
  • Python Json模块中dumps、loads、dump、load函数介绍

    2021-03-20 11:21:20
  • 一篇文章搞定数据库连接池

    2024-01-19 02:55:49
  • pandas.DataFrame.to_json按行转json的方法

    2022-11-09 11:24:23
  • 详解vue+webpack+express中间件接口使用

    2024-04-29 13:11:10
  • 学点简单的Django之第一个Django程序的实现

    2021-03-23 05:10:59
  • 可能是最全面的 Python 字符串拼接总结【收藏】

    2023-10-06 14:29:20
  • python递归实现快速排序

    2023-08-26 22:46:27
  • Vue按回车键进行搜索的实现方式

    2024-05-05 09:06:27
  • 使用python生成云词图实现画红楼梦词云图

    2022-07-19 00:21:56
  • python发送伪造的arp请求

    2022-11-24 00:47:35
  • 高手进阶:网页设计中的文字运用

    2008-10-05 08:58:00
  • C#连接db2数据库的实现方法

    2024-01-19 07:00:51
  • python 识别登录验证码图片功能的实现代码(完整代码)

    2021-03-14 23:03:40
  • asp之家 网络编程 m.aspxhome.com