网络编程
位置:首页>> 网络编程>> Python编程>> Python迭代器的实现原理

Python迭代器的实现原理

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

标签:Python,迭代器,原理

前言:

在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

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com