精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Python 迭代器是怎么實(shí)現(xiàn)的?

開發(fā) 前端
通過探究迭代器,我們?cè)俅误w會(huì)到了 Python 的設(shè)計(jì)哲學(xué),雖然一切皆對(duì)象,但是拿到的都是對(duì)象的指針。像變量、函數(shù)參數(shù)等,它們存儲(chǔ)的都不是對(duì)象本身,而是對(duì)象的泛型指針。而基于 PyObject * 和 ob_type,Python 巧妙地實(shí)現(xiàn)了多態(tài)。

楔子

只要類型對(duì)象實(shí)現(xiàn)了 __iter__,那么它的實(shí)例對(duì)象就被稱為可迭代對(duì)象(Iterable),比如字符串、元組、列表、字典、集合等等。而整數(shù)、浮點(diǎn)數(shù),由于其類型對(duì)象沒有實(shí)現(xiàn) __iter__,所以它們不是可迭代對(duì)象。

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

可迭代對(duì)象的一大特點(diǎn)是它可以被 for 循環(huán)遍歷,但能被 for 循環(huán)遍歷的則不一定是可迭代對(duì)象。我們舉個(gè)例子:

class A:

    def __getitem__(self, item):
        return f"參數(shù) item: {item}"

a = A()
# 內(nèi)部定義了 __getitem__
# 首先可以讓實(shí)例對(duì)象像字典一樣訪問屬性
print(a["name"])  # 參數(shù) item: name
print(a["satori"])  # 參數(shù) item: satori

# 此外還可以像可迭代對(duì)象一樣被 for 循環(huán)
# 循環(huán)的時(shí)候會(huì)自動(dòng)給 item 傳值:0 1 2 3 ...
# 如果內(nèi)部出現(xiàn)了 StopIteration,循環(huán)結(jié)束
# 否則會(huì)一直循環(huán)下去,這里我們手動(dòng) break
for idx, val in enumerate(a):
    print(val)
    if idx == 5:
        break
"""
參數(shù) item: 0
參數(shù) item: 1
參數(shù) item: 2
參數(shù) item: 3
參數(shù) item: 4
參數(shù) item: 5
"""

所以實(shí)現(xiàn)了 __getitem__ 的類的實(shí)例,也是可以被 for 循環(huán)的,但它并不是可迭代對(duì)象。

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

總之判斷一個(gè)對(duì)象是否是可迭代對(duì)象,就看它的類型對(duì)象有沒有實(shí)現(xiàn) __iter__。可迭代對(duì)象我們知道了,那什么是迭代器呢?很簡單,調(diào)用可迭代對(duì)象的 __iter__ 方法,得到的就是迭代器。

迭代器的創(chuàng)建

不同類型的對(duì)象,都有自己的迭代器,舉個(gè)栗子。

data = [1, 2, 3]
# 底層調(diào)用的其實(shí)是 list.__iter__(data)
# 或者說 PyList_Type.tp_iter(data)
it = data.__iter__()
print(it)
"""
<list_iterator object at 0x102c1cf10>
"""
print(str.__iter__(""))
"""
<str_iterator object at 0x100e623b0>
"""
print(tuple.__iter__(()))
"""
<tuple_iterator object at 0x100e623b0>
"""
# 不難發(fā)現(xiàn),迭代器的種類有非常多
# 比如 list_iterator、str_iterator、tuple_iterator 等等

迭代器也是可迭代對(duì)象,只不過迭代器內(nèi)部的 __iter__ 返回的還是它本身。當(dāng)然啦,在創(chuàng)建迭代器的時(shí)候,我們更常用內(nèi)置函數(shù) iter。

data = [1, 2, 3]
# 等價(jià)于 type(data).__iter__(data)
it = iter(data)

但是 iter 函數(shù)還有一個(gè)鮮為人知的用法,我們來看一下:

val = 0

def foo():
    global val
    val += 1
    return val

# iter 可以接收一個(gè)參數(shù): iter(可迭代對(duì)象)
# iter 也可以接收兩個(gè)參數(shù): iter(可調(diào)用對(duì)象, value)
for i in iter(foo, 5):
    print(i)
"""
1
2
3
4
"""

進(jìn)行迭代的時(shí)候,會(huì)不停地調(diào)用可調(diào)用對(duì)象,直到返回值等于傳遞的第二個(gè)參數(shù) value(在底層被稱為哨兵),然后終止迭代。我們看一下 iter 函數(shù)的底層實(shí)現(xiàn)。

// Python/clinic/bltinmodule.c.h
static PyObject *
builtin_iter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
    PyObject *return_value = NULL;
    PyObject *object;
    PyObject *sentinel = NULL;
    // 內(nèi)置函數(shù) iter 接收 1 ~ 2 個(gè)參數(shù)
    if (!_PyArg_CheckPositional("iter", nargs, 1, 2)) {
        goto exit;
    }
    // 如果 nargs 小于 2,那么 args[0] 是可迭代對(duì)象
    // 如果 nargs 等于 2,那么 args[0] 是可調(diào)用對(duì)象
    object = args[0];
    if (nargs < 2) {
        goto skip_optional;
    }
    sentinel = args[1];
skip_optional:
    // 具體實(shí)現(xiàn)由 builtin_iter_impl 負(fù)責(zé)
    // 它會(huì)調(diào)用可迭代對(duì)象的 __iter__ 方法,返回迭代器
    return_value = builtin_iter_impl(module, object, sentinel);

exit:
    return return_value;
}

// Python/bltinmodule.c
static PyObject *
builtin_iter_impl(PyObject *module, PyObject *object, PyObject *sentinel)
{
    // 如果哨兵為空,說明只傳了一個(gè)參數(shù),那么該參數(shù)應(yīng)該是可迭代對(duì)象
    if (sentinel == NULL)
        // 調(diào)用 PyObject_GetIter 獲取對(duì)象的迭代器
        return PyObject_GetIter(object);
    // 如果哨兵不為空,那么第一個(gè)參數(shù)應(yīng)該是可調(diào)用對(duì)象
    // 這里進(jìn)行檢測(cè),如果不是,拋出 TypeError
    if (!PyCallable_Check(object)) {
        PyErr_SetString(PyExc_TypeError,
                        "iter(object, sentinel): object must be callable");
        return NULL;
    }
    // 一會(huì)兒單獨(dú)解釋
    return PyCallIter_New(object, sentinel);
}

以上就是 iter 函數(shù)的內(nèi)部邏輯,既可以接收一個(gè)參數(shù),也可以接收兩個(gè)參數(shù)。這里我們只看接收一個(gè)可迭代對(duì)象的情況,所以核心就在 PyObject_GetIter 函數(shù)里面,它是根據(jù)可迭代對(duì)象生成迭代器的關(guān)鍵,我們來看一下它的邏輯是怎么樣的?

// Objects/abstract.c
PyObject *
PyObject_GetIter(PyObject *o)
{
    // 獲取可迭代對(duì)象的類型對(duì)象,比如 o 是列表,那么 t 就是 list
    PyTypeObject *t = Py_TYPE(o);
    // 我們說類型對(duì)象定義的操作,決定了實(shí)例對(duì)象的行為
    // 實(shí)例對(duì)象調(diào)用的那些方法都是定義在類型對(duì)象里面的
    // 還是那句話:obj.func() 等價(jià)于 type(obj).func(obj)
    getiterfunc f;
    
    // 所以這里是獲取類型對(duì)象的 tp_iter 字段
    // 也就是 Python 中的 __iter__
    f = t->tp_iter;
    // 如果 f 為 NULL,說明類型對(duì)象的內(nèi)部沒有定義 __iter__ 
    // 像 str、tuple、list 等類型對(duì)象,它們的 tp_iter 字段都是不為 NULL 的
    if (f == NULL) {
        // 如果 tp_iter 為 NULL,那么解釋器會(huì)退而求其次
        // 檢測(cè)該類型對(duì)象中是否定義了 __getitem__
        // 如果定義了,那么直接調(diào)用 PySeqIter_New,創(chuàng)建 seqiterobject 對(duì)象
        // 下面的 PySequence_Check 函數(shù)負(fù)責(zé)檢測(cè)類型對(duì)象是否實(shí)現(xiàn)了 __getitem__
        // __getitem__ 對(duì)應(yīng) tp_as_sequence->sq_item
        if (PySequence_Check(o))
            return PySeqIter_New(o);
        // 走到這里說明該類型對(duì)象既沒有 __iter__、也沒有 __getitem__
        // 因此它的實(shí)例對(duì)象不具備可迭代的性質(zhì),于是拋出異常
        return type_error("'%.200s' object is not iterable", o);
    }
    else {
        // 否則說明定義了 __iter__,于是直接進(jìn)行調(diào)用
        // Py_TYPE(o)->tp_iter(o) 返回對(duì)應(yīng)的迭代器
        PyObject *res = (*f)(o);
        // 但如果返回值 res 不為 NULL、并且還不是迭代器
        // 證明 __iter__ 的返回值有問題,于是拋出異常
        if (res != NULL && !PyIter_Check(res)) {
            PyErr_Format(PyExc_TypeError,
                         "iter() returned non-iterator "
                         "of type '%.100s'",
                         Py_TYPE(res)->tp_name);
            Py_SETREF(res, NULL);
        }
        // 返回 res
        return res;
    }
}

以上便是 iter 函數(shù)的底層實(shí)現(xiàn),還是很簡單的。然后是里面的 __getitem__,我們說如果類型對(duì)象內(nèi)部沒有定義 __iter__,那么解釋器會(huì)退而求其次,檢測(cè)內(nèi)部是否定義了 __getitem__。

因此以上就是迭代器的創(chuàng)建過程,每個(gè)可迭代對(duì)象都有自己的迭代器,而迭代器本質(zhì)上就是對(duì)原始數(shù)據(jù)的一層封裝罷了。

迭代器的底層結(jié)構(gòu)

由于迭代器的種類非常多,字符串、元組、列表等等,都有自己的迭代器,這里就不一一介紹了。我們就以列表的迭代器為例,看看迭代器在底層的結(jié)構(gòu)是怎么樣的。

// Objects/listobject.c

// 列表迭代器的類型對(duì)象為 <class 'list_iterator'>
// 但這個(gè)類,解釋器并沒有暴露給我們,所以需要通過 type 獲取
// 然后它的 tp_basicsize 字段為 sizeof(_PyListIterObject)
// 這就說明列表迭代器在底層由 _PyListIterObject 結(jié)構(gòu)體表示
PyTypeObject PyListIter_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "list_iterator",                            /* tp_name */
    sizeof(_PyListIterObject),                  /* tp_basicsize */
    0,                                          /* tp_itemsize */
    // ...
};

// Include/internal/pycore_list.h
typedef struct {
    PyObject_HEAD
    Py_ssize_t it_index;
    // 指向創(chuàng)建該迭代器的列表
    PyListObject *it_seq; 
} _PyListIterObject;

所以迭代器就是基于可迭代對(duì)象進(jìn)行了一層簡單的封裝,所謂元素迭代本質(zhì)上還是基于索引,并且每迭代一次,索引就自增 1。一旦出現(xiàn)索引越界,就將 it_seq 設(shè)置為 NULL,表示迭代器迭代完畢。

我們實(shí)際演示一下:

from ctypes import *

class PyObject(Structure):
    _fields_ = [
        ("ob_refcnt", c_ssize_t),
        ("ob_size", c_void_p)
    ]

class ListIterObject(PyObject):
    _fields_ = [
        ("it_index", c_ssize_t),
        ("it_seq", POINTER(PyObject))
    ]

it = iter([1, 2, 3])
it_obj = ListIterObject.from_address(id(it))

# it_seq 指向列表 [1, 2, 3],it_index 初始為 0
print(it_obj.it_index)  # 0
# 進(jìn)行迭代
next(it)
# 索引自增 1,此時(shí) it_index 等于 1
print(it_obj.it_index)  # 1
# 再次迭代
next(it)
# 此時(shí) it_index 等于 2
print(it_obj.it_index)  # 2
# 再次迭代
next(it)
# 此時(shí) it_index 等于 3
print(it_obj.it_index)  # 3

當(dāng) it_index 為 3 的時(shí)候,如果再次迭代,那么底層會(huì)發(fā)現(xiàn) it_index 已超過最大索引,于是知道迭代器已經(jīng)迭代完畢了。因此會(huì)將 it_seq 設(shè)置為 NULL,并拋出 StopIteration。如果是 for 循環(huán),那么會(huì)自動(dòng)捕獲此異常,然后停止循環(huán)。

所以這就是迭代器,真的沒有想象中的那么神秘,甚至在知道它的實(shí)現(xiàn)原理之后,還覺得有點(diǎn) low,因?yàn)榫褪菍⒃紨?shù)據(jù)包了一層,加了一個(gè)索引而已。所謂的迭代仍然是基于索引來做的,并且每迭代一次,索引就自增 1。當(dāng)索引超出范圍時(shí),證明迭代完畢了,于是將 it_seq 字段設(shè)置為 NULL,拋出 StopIteration。

迭代器是怎么迭代元素的

迭代器的創(chuàng)建我們知道了,那么它是怎么迭代元素的呢?首先迭代元素可以通過 next 函數(shù),當(dāng)然它本質(zhì)上也是調(diào)用了對(duì)象的 __next__ 方法。

// Python/clinic/bltinmodule.c.h
static PyObject *
builtin_next(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
    PyObject *return_value = NULL;
    PyObject *iterator;
    PyObject *default_value = NULL;
    // 同樣接收 1 ~ 2 個(gè)參數(shù)
    // 因?yàn)檎{(diào)用 next 函數(shù)時(shí),可以傳入一個(gè)默認(rèn)值
    // 表示當(dāng)?shù)鳑]有元素可以迭代的時(shí)候,會(huì)返回指定的默認(rèn)值
    if (!_PyArg_CheckPositional("next", nargs, 1, 2)) {
        goto exit;
    }
    // 迭代器
    iterator = args[0];
    if (nargs < 2) {
        goto skip_optional;
    }
    // 默認(rèn)值
    default_value = args[1];
skip_optional:
    return_value = builtin_next_impl(module, iterator, default_value);

exit:
    return return_value;
}

// Python/bltinmodule.c
static PyObject *
builtin_next_impl(PyObject *module, PyObject *iterator,
                  PyObject *default_value)
{
    PyObject *res;
    // 第一個(gè)參數(shù)必須是迭代器,否則拋出 TypeError
    if (!PyIter_Check(iterator)) {
        PyErr_Format(PyExc_TypeError,
            "'%.200s' object is not an iterator",
            Py_TYPE(iterator)->tp_name);
        return NULL;
    }
    // Py_TYPE(iterator) 表示獲取類型對(duì)象,也就是該迭代器的類型
    // 當(dāng)然具體類型是哪一種并不確定,可能是列表迭代器、元組迭代器、字符串迭代器等等
    // 然后再獲取 tp_iternext 字段,相當(dāng)于 __next__
    // 拿到函數(shù)指針之后,傳入迭代器進(jìn)行調(diào)用
    res = (*Py_TYPE(iterator)->tp_iternext)(iterator);
    // 如果 res 不為 NULL, 那么證明迭代到值了, 直接返回
    if (res != NULL) {
        return res;
    } else if (default_value != NULL) {
        // 否則的話,說明沒有迭代到值(返回 NULL),那么這時(shí)候有兩種情況
        // 1)迭代器已耗盡,2)在迭代過程中出現(xiàn)異常
        // 那么判斷 default_value,如果不為 NULL,說明設(shè)置了默認(rèn)值
        if (PyErr_Occurred()) {
            // 檢測(cè)異常是不是迭代完畢時(shí)(或者手動(dòng) raise)產(chǎn)生的 StopIteration 異常
            if(!PyErr_ExceptionMatches(PyExc_StopIteration))
                // 如果不是,說明程序的邏輯有問題,直接 return NULL,結(jié)束執(zhí)行
                // 然后在 Python 里面我們會(huì)看到打印到 stderr 中的異常信息
                return NULL;
            // 如果異常是 StopIteration,證明迭代完畢了
            // 但我們?cè)O(shè)置了默認(rèn)值,那么就應(yīng)該返回默認(rèn)值
            // 而不應(yīng)該拋出 StopIteration,于是將異常回溯棧給清空
            PyErr_Clear();
        }
        // 增加 default_value 的引用計(jì)數(shù),然后返回
        return Py_NewRef(default_value);
    } else if (PyErr_Occurred()) {
        // 走到這里說明 res == NULL,并且沒有指定默認(rèn)值
        // 那么當(dāng)發(fā)生異常時(shí),將異常直接拋出
        return NULL;
    } else {
        // 都不是的話,直接拋出 StopIteration
        PyErr_SetNone(PyExc_StopIteration);
        return NULL;
    }
}

以上就是 next 函數(shù)的背后邏輯,實(shí)際上還是調(diào)用了迭代器的 __next__ 方法。

data = [1, 2, 3]
it = iter(data)
# 然后迭代,等價(jià)于 next(it)
print(type(it).__next__(it))  # 1
print(type(it).__next__(it))  # 2
print(type(it).__next__(it))  # 3
# 但是 next 可以指定默認(rèn)值
# 如果不指定默認(rèn)值,或者還是 type(it).__next__(it)
# 那么就會(huì)報(bào)錯(cuò),拋出 StopIteration
print(next(it, 666))  # 666

以上就是元素的迭代,由于內(nèi)置函數(shù) next 還可以指定一個(gè)默認(rèn)值,所以更強(qiáng)大一些。當(dāng)然在不指定默認(rèn)值的情況下,next(it) 和 type(it).__next__(it) 最終是殊途同歸的。

我們?nèi)砸粤斜淼牡鳛槔纯?nbsp;__next__ 的具體實(shí)現(xiàn)。但是要想找到具體實(shí)現(xiàn),首先要找到它的類型對(duì)象。

我們看到 tp_iternext 字段指向了 listiter_next,證明迭代的時(shí)候調(diào)用的是這個(gè)函數(shù)。

// Objects/listobject.c
static PyObject *
listiter_next(_PyListIterObject *it)
{
    // 迭代器只是對(duì)可迭代對(duì)象的一層封裝
    // 如果是列表的迭代器,那么內(nèi)部的 it_seq 字段便指向列表
    PyListObject *seq;
    PyObject *item;

    assert(it != NULL);
    // 如果 it->it_seq 等于 NULL,說明迭代器已經(jīng)迭代完畢了
    // 從這里也能看出迭代器不能二次循環(huán)迭代
    seq = it->it_seq;
    if (seq == NULL)
        return NULL;
    assert(PyList_Check(seq));
    // 如果 it->it_index 小于列表的長度
    if (it->it_index < PyList_GET_SIZE(seq)) {
        // 那么獲取元素
        item = PyList_GET_ITEM(seq, it->it_index);
        // it_index 自增 1
        ++it->it_index;
        // 增加元素的引用計(jì)數(shù),并返回
        return Py_NewRef(item);
    }
    // 否則說明 it_index 已經(jīng)達(dá)到了列表的長度
    // 再迭代就索引越界了,而對(duì)于迭代器來說
    // 當(dāng) it_index 等于列表長度時(shí),就證明所有元素都迭代完畢了
    it->it_seq = NULL;  // 將 it_seq 設(shè)置為 NULL
    Py_DECREF(seq);
    return NULL;
}

顯然這和之前分析的是一樣的,以上我們就以列表為例,考察了迭代器的實(shí)現(xiàn)原理和元素迭代的具體過程。當(dāng)然其它對(duì)象也有自己的迭代器,有興趣可以自己看一看,實(shí)現(xiàn)方式都大同小異。

小結(jié)

通過探究迭代器,我們?cè)俅误w會(huì)到了 Python 的設(shè)計(jì)哲學(xué),雖然一切皆對(duì)象,但是拿到的都是對(duì)象的指針。像變量、函數(shù)參數(shù)等,它們存儲(chǔ)的都不是對(duì)象本身,而是對(duì)象的泛型指針。而基于 PyObject * 和 ob_type,Python 巧妙地實(shí)現(xiàn)了多態(tài)。

不管變量 obj 指向什么樣的可迭代對(duì)象,都可以交給 iter 函數(shù),會(huì)調(diào)用類型對(duì)象內(nèi)部的 __iter__(底層對(duì)應(yīng) tp_iter 字段),得到迭代器。不管變量 it 指向什么樣的迭代器,都可以交給 next 函數(shù)進(jìn)行迭代,會(huì)調(diào)用迭代器的類型對(duì)象的 __next__(底層對(duì)應(yīng) tp_iternext 字段),將值迭代出來。

至于 __iter__ 和 __next__ 本身,每個(gè)迭代器都會(huì)有,我們這里只以列表的迭代器為例。所以這是不是實(shí)現(xiàn)了多態(tài)呢?

這就是 Python 的設(shè)計(jì)哲學(xué),變量只是一個(gè)指針,傳遞變量的時(shí)候相當(dāng)于傳遞指針(將指針拷貝一份),但是操作一個(gè)變量的時(shí)候會(huì)自動(dòng)操作變量(指針)指向的內(nèi)存。

對(duì)了,我們說 iter 函數(shù)如果接收兩個(gè)參數(shù),那么第一個(gè)參數(shù)要是 callable,第二個(gè)參數(shù)是哨兵。迭代時(shí)會(huì)調(diào)用 callable,當(dāng)返回值等于哨兵時(shí),迭代結(jié)束,那么它的底層是怎么實(shí)現(xiàn)的呢?這里簡單補(bǔ)充一下。

// Python/bltinmodule.c
static PyObject *
builtin_iter_impl(PyObject *module, PyObject *object, PyObject *sentinel)
{
    if (sentinel == NULL)
        return PyObject_GetIter(object);
    if (!PyCallable_Check(object)) {
        PyErr_SetString(PyExc_TypeError,
                        "iter(object, sentinel): object must be callable");
        return NULL;
    }
    // 如果 sentinel 不等于 NULL,會(huì)調(diào)用 PyCallIter_New
    return PyCallIter_New(object, sentinel);
}


// Objects/iterobject.c
typedef struct {
    PyObject_HEAD
    PyObject *it_callable; 
    PyObject *it_sentinel; 
} calliterobject;

PyObject *
PyCallIter_New(PyObject *callable, PyObject *sentinel)
{
    // iter(callable, value) 會(huì)返回一個(gè) <class 'callable_iterator'> 實(shí)例
    // 在底層由 calliterobject 結(jié)構(gòu)體實(shí)現(xiàn)
    calliterobject *it;
    // 為 calliterobject 實(shí)例申請(qǐng)內(nèi)存
    it = PyObject_GC_New(calliterobject, &PyCallIter_Type);
    if (it == NULL)
        return NULL;
    // 初始化字段
    it->it_callable = Py_NewRef(callable);
    it->it_sentinel = Py_NewRef(sentinel);
    _PyObject_GC_TRACK(it);
    return (PyObject *)it;
}

// 再來看看迭代過程
static PyObject *
calliter_iternext(calliterobject *it)
{
    PyObject *result;
    // 如果 it_callable 字段為空,說明迭代結(jié)束,不能再次迭代
    if (it->it_callable == NULL) {
        return NULL;
    }
    // 調(diào)用 it_callable,拿到返回值 result
    result = _PyObject_CallNoArgs(it->it_callable);
    if (result != NULL && it->it_sentinel != NULL){
        int ok;
        // 如果 result 和哨兵相等,那么 ok == 1,否則 ok == 0
        ok = PyObject_RichCompareBool(it->it_sentinel, result, Py_EQ);
        // 如果 ok == 0,說明兩者不相等,那么返回 result
        if (ok == 0) {
            return result; 
        }
        // 如果返回值和哨兵相等,那么迭代結(jié)束
        // 減少引用計(jì)數(shù),并將 it_callable 和 it_sentinel 字段設(shè)置為 NULL
        if (ok > 0) {
            Py_CLEAR(it->it_callable);
            Py_CLEAR(it->it_sentinel);
        }
    }
    else if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
        // 如果函數(shù)拋出了 StopIteration 異常,同樣視為迭代結(jié)束
        PyErr_Clear();
        Py_CLEAR(it->it_callable);
        Py_CLEAR(it->it_sentinel);
    }
    Py_XDECREF(result);
    return NULL;
}

以上就是 Python 迭代器的相關(guān)內(nèi)容,當(dāng)然我們也完全可以自己封裝一個(gè)迭代器,有興趣可以試一下。

責(zé)任編輯:武曉燕 來源: 古明地覺的編程教室
相關(guān)推薦

2016-03-28 10:39:05

Python迭代迭代器

2021-11-28 08:03:41

Python迭代器對(duì)象

2022-09-14 08:01:36

JoinMySQL迭代器

2023-11-23 19:30:35

Python編程語言

2024-11-28 10:32:32

2022-10-26 08:25:06

Python編程迭代器

2017-06-26 16:26:15

Python迭代對(duì)象迭代器

2024-05-31 09:31:00

2024-05-31 08:38:35

Python浮點(diǎn)數(shù)屬性

2009-02-17 18:52:06

網(wǎng)絡(luò)虛擬化路由系統(tǒng)數(shù)據(jù)中心

2021-08-30 22:38:47

VscodeMarkdown預(yù)覽

2013-08-19 16:44:15

.Net

2020-11-24 08:00:22

JavaScript對(duì)象迭代器

2020-11-16 08:10:04

ES6迭代器JavaScript

2021-02-02 10:53:16

Python編程開發(fā)

2024-11-01 16:05:26

2021-09-07 09:18:18

Kubernetes負(fù)載均衡服務(wù)發(fā)現(xiàn)

2022-04-01 07:14:13

模塊Pythonimport

2014-07-31 10:10:53

全息影像手機(jī)數(shù)碼

2024-01-15 08:08:27

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美疯狂做受xxxx高潮| 一区二区三区日韩| 国产精品国产三级国产专播精品人| 中文文字幕文字幕高清| 国产夫妻在线播放| 精品在线播放午夜| 欧美久久精品一级黑人c片| 91人妻一区二区三区| a毛片不卡免费看片| 91在线视频播放| 国产成人亚洲综合91精品| 免费黄在线观看| 久久久久久久久久久久电影| 亚洲风情在线资源站| 久久婷婷国产综合尤物精品| 男操女视频网站| 亚洲激情久久| 亚洲欧美日韩视频一区| 欧美日韩一区二区在线免费观看| 思思99re6国产在线播放| 国产成人综合亚洲91猫咪| 91国产美女视频| 一级黄色片大全| 国产精品一区三区在线观看| 亚洲v日本v欧美v久久精品| 久久精品国产第一区二区三区最新章节 | 天天爽夜夜爽夜夜爽精品视频| 国产超碰91| 日韩国产成人在线| 亚洲天堂男人| 色综合伊人色综合网| 国内精品免费视频| 久久婷婷五月综合色丁香| 亚洲欧美日韩国产手机在线| 国内精品视频免费| 国产精品久久影视| 首页综合国产亚洲丝袜| 欧美精品中文字幕一区| 国产传媒在线看| 国偷自产av一区二区三区| 欧美日韩国产首页在线观看| 国产香蕉一区二区三区| 国产资源在线看| 大陆成人av片| 91麻豆桃色免费看| 亚洲国产成人精品女人久久| 亚洲黄色影片| 欧美成人精品xxx| 高清国产在线观看| 小说区图片区色综合区| 欧美成人a视频| 91看片破解版| 欧美大胆成人| 懂色aⅴ精品一区二区三区蜜月 | 热re99久久精品国产66热| 国产又粗又硬又长又爽| 成人亚洲一区二区| 亚洲香蕉av在线一区二区三区| 国产真实乱人偷精品| 91蜜桃臀久久一区二区| 欧美高清www午色夜在线视频| 日本成人在线免费视频| 捆绑调教日本一区二区三区| 亚洲午夜久久久久久久久电影院| 五月天综合婷婷| 永久免费av在线| 国产精品色婷婷| 亚洲精品一区二| 成年人在线看| 国产日韩欧美高清在线| 日韩av一级大片| 日av在线播放| 国产婷婷一区二区| 青青成人在线| 丁香婷婷在线| 国产精品久久久久久久岛一牛影视| 亚洲精品乱码视频| 黄色网页在线免费看| 亚洲欧洲精品一区二区三区| 一区一区视频| 啪啪免费视频一区| 亚洲免费在线播放| 日本福利视频网站| av资源网在线播放| 一卡二卡三卡日韩欧美| 欧美精品久久久久久久久久久| xxxx在线视频| 日韩欧美精品中文字幕| 精品999在线| 成人在线啊v| 精品国产成人在线影院 | 久久三级中文| 日韩一级片网址| 亚洲自拍偷拍精品| 西瓜成人精品人成网站| 亚洲欧美日韩一区在线| 99自拍偷拍视频| 亚洲视频一二| 青青久久aⅴ北条麻妃| 中文字幕乱码人妻二区三区| 韩国成人精品a∨在线观看| 国产乱人伦真实精品视频| 精品国产乱码一区二区三| 99久久亚洲一区二区三区青草| 秋霞毛片久久久久久久久| 米奇777四色精品人人爽| 亚洲午夜精品一区二区三区他趣| a√天堂在线观看| 成人四虎影院| 欧美精品一区二区不卡| 婷婷色一区二区三区| 国产精品一区高清| 久久99精品国产99久久6尤物| 久久久久久久久久久影院| 另类小说综合欧美亚洲| 国产精品一级久久久| 成人精品一区二区三区校园激情| 一区二区三区免费看视频| heyzo国产| 精品一区二区三区中文字幕视频 | 国产免费一区二区三区四区五区| 久久精品av麻豆的观看方式| 国产成人精品日本亚洲11| 粉嫩一区二区三区国产精品| 亚洲日本在线a| 国产成人亚洲精品无码h在线| 国产aa精品| 亚洲精品综合精品自拍| 欧美精品一级片| 日韩电影一区二区三区| 国产一级二级三级精品| 成人区精品一区二区不卡| 色呦呦日韩精品| 美国黄色一级视频| 香蕉av一区二区| 国产精品高潮视频| 日本高清视频www| 亚洲视频一区二区在线观看| 欧美亚洲日本在线观看| 欧美变态网站| 欧美高清一级大片| 亚洲一区二区影视| 国产三级精品在线| 日韩欧美亚洲天堂| 成人h动漫免费观看网站| 久久色精品视频| 丁香社区五月天| av动漫一区二区| 又大又硬又爽免费视频| 国产精品va视频| 最近2019中文字幕mv免费看 | 日本中文字幕有码| 91精品亚洲| 国产在线视频91| 婷婷在线视频| 日本黄色一区二区| 波多野结衣办公室33分钟| 欧美黄在线观看| 亚洲一区制服诱惑| 高潮毛片在线观看| 3d成人h动漫网站入口| 亚洲高潮女人毛茸茸| 免费观看一级特黄欧美大片| 五月天综合网| 国产精品4hu.www| www国产亚洲精品久久网站| 免费黄色网址在线| 久久夜色精品国产噜噜av| 青青草视频在线视频| 日本精品一区二区三区在线观看视频| 中文字幕日韩在线播放| 中文字幕一区二区人妻| 国产欧美日韩在线观看| 国产又猛又黄的视频| 亚洲婷婷丁香| 国产精品看片资源| 久久亚洲天堂| 日韩三级视频中文字幕| 国产精品成人久久| 91麻豆成人久久精品二区三区| 亚洲午夜精品久久久久久人妖| 小说区图片区色综合区| 国产成人免费av电影| 看黄网站在线| 亚洲精品久久久久久久久久久| 波多野结衣毛片| 亚洲三级在线播放| 粉嫩av懂色av蜜臀av分享| 日本欧美一区二区| 久久av综合网| 日韩成人影院| 国内一区二区在线视频观看 | 国产日韩欧美中文| 福利写真视频网站在线| 亚洲一级一级97网| 国产77777| 欧美久久久久中文字幕| 好吊妞视频一区二区三区| 国产精品麻豆视频| 日本护士做爰视频| 国产综合成人久久大片91| 日本www在线视频| 国产精品久久久乱弄| 国产成人精品福利一区二区三区| 国产精品蜜月aⅴ在线| 亚洲2020天天堂在线观看| 欧美69xxx| 亚洲人成网站777色婷婷| 亚洲精品成人电影| 欧美日韩国产精品成人| 天天干天天干天天干天天| 夜夜揉揉日日人人青青一国产精品| 亚洲一区二区三区蜜桃| 成人精品视频一区二区三区| 天天操天天干天天做| 视频在线观看一区二区三区| 欧日韩免费视频| 欧美va天堂在线| 综合操久久久| 精品国产一区二区三区四区| 精品久久久久亚洲| 亚洲性视频在线| 亚洲一区二区三区香蕉| 成人免费一区| 国产精品激情自拍| 在线观看爽视频| 91国产在线精品| 96av在线| 国内精品久久久| 四虎亚洲精品| 欧美人在线观看| 成人在线播放免费观看| 日韩小视频在线| 男人的天堂在线免费视频| 亚洲成人av片在线观看| 午夜精品久久久久久久96蜜桃| 欧美一区二区免费视频| 国产精品永久久久久久久久久| 欧美在线观看一区二区| 日韩黄色一级视频| 色婷婷综合五月| 日本丰满少妇做爰爽爽| 欧美在线一二三四区| 欧美国产一级片| 欧美日韩中文国产| 中文字幕一区二区三区免费看| 日本韩国一区二区三区| 夜夜爽妓女8888视频免费观看| 色狠狠av一区二区三区| 国产又粗又猛又爽又| 欧美日韩视频在线第一区 | 麻豆成人久久精品二区三区红| 99久久久无码国产精品6| 丝袜亚洲另类欧美| a在线观看免费视频| 精品一二三四区| 久久精品国产99久久99久久久| 国产毛片精品一区| 亚洲av无码成人精品区| 成人美女在线视频| 制服丝袜第一页在线观看| 91免费精品国自产拍在线不卡| 成年人在线观看av| 国产欧美一区在线| 大地资源高清在线视频观看| 亚洲一区二区三区国产| 国产精品久久久久久99| 欧美午夜免费电影| 99免费在线视频| 日韩电影在线观看中文字幕| 久青草国产在线| 日韩在线播放视频| 高清电影在线观看免费| 国产精品91在线观看| 视频欧美精品| 国产在线精品一区二区三区》| 免费电影一区二区三区| 在线综合视频网站| 影音先锋久久久| 美女黄色片视频| 国产成人午夜精品影院观看视频| yy1111111| 国产精品成人免费| 国产乡下妇女做爰| 欧美日韩五月天| 日韩在线观看视频网站| 一区二区三区天堂av| 先锋成人av| 国产成人一区二区| av日韩在线播放| 天天综合狠狠精品| 亚洲三级观看| 日韩在线一区视频| 91网站最新网址| 国产又黄又爽又无遮挡| 一本色道久久加勒比精品 | 热99这里只有精品| 久久精品国产99国产| 午夜视频在线观看国产| ...中文天堂在线一区| 国产91精品一区| 日韩三级在线观看| 欧美精品videos另类| 欧美在线不卡区| 日韩中文字幕无砖| 亚洲aⅴ天堂av在线电影软件| 亚洲一级特黄| www.成人黄色| 国产三级精品三级| 亚洲国产精品午夜在线观看| 欧美日韩国产首页| 黄色网址在线播放| 97avcom| 亚洲精品一区二区三区中文字幕| 日韩高清专区| 国产精品日韩久久久| 国产精品91av| 中文字幕永久在线不卡| 男人天堂2024| 日韩av在线一区二区| 日本在线观看大片免费视频| 国产欧美日韩最新| 成人直播大秀| 国产又黄又猛又粗又爽的视频| 91蜜桃婷婷狠狠久久综合9色| 国产在线观看免费视频今夜| 欧美一区二区性放荡片| 欧美一级二级三级区| 国产精品一区久久| 精品久久91| 亚洲成人av免费看| 国产亚洲精品免费| 91精品国产高清一区二区三密臀| 亚洲福利在线观看| 精品丝袜在线| 精品麻豆av| 性欧美xxxx大乳国产app| 国产精品麻豆入口| 午夜精品一区二区三区电影天堂| 午夜精品久久久久久久99| 欧美成人午夜视频| 日韩精品免费视频一区二区三区| 懂色av一区二区三区四区五区| 久99久精品视频免费观看| www日韩在线| 欧美成人乱码一区二区三区| 日韩专区av| 国产成人精品免费视频大全最热| 一区在线播放| 给我免费观看片在线电影的| 欧美日韩国产精品专区| 五月天婷婷在线播放| 日本一区二区在线播放| 经典一区二区| 九九热精品在线播放| 亚洲欧洲日韩在线| 性生交生活影碟片| 国内精品久久久久影院 日本资源| 国产精品久久久久久久久久白浆| av高清在线免费观看| www激情久久| 中文字幕在线播放日韩| 久久国产精品久久久| 超碰精品在线| 日韩精品一区中文字幕| 国产精品每日更新| 亚洲av无码国产精品久久不卡| 欧美精品aaa| 蜜桃成人av| 日本中文字幕精品—区二区| 亚洲色图在线视频| 日本美女一级片| 国产99在线|中文| 婷婷久久一区| 国产精品久久久免费观看| 欧美伊人精品成人久久综合97| 黄av在线播放| 好吊妞www.84com只有这里才有精品| 久久不射中文字幕| 天天看天天摸天天操| 亚洲福利影片在线| 国产一区精品福利| 美女黄色免费看| 国产日韩欧美精品电影三级在线| 国产喷水福利在线视频| 欧美一级电影久久| 五月天久久网站| 精品人妻少妇嫩草av无码| 91麻豆精品国产91久久久资源速度| 暧暧视频在线免费观看| 一区二区三区的久久的视频| 成人av网站在线观看免费| 波多野结衣在线观看视频| 欧美成人小视频| 精品久久久亚洲| 国产精久久久久| 欧美日韩久久久一区| a级片免费在线观看| 在线观看日韩片| 久久丝袜美腿综合|