How does Python manage a 'for' loop internally?

前端 未结 4 1973
臣服心动
臣服心动 2021-02-02 12:55

I\'m trying to learn Python, and I started to play with some code:

a = [3,4,5,6,7]
for b in a:
    print(a)
    a.pop(0)
<         


        
4条回答
  •  别跟我提以往
    2021-02-02 13:33

    We can easily see the sequence of events by using a little helper function foo:

    def foo():
        for i in l:
            l.pop()
    

    and dis.dis(foo) to see the Python byte-code generated. Snipping away the not-so-relevant opcodes, your loop does the following:

              2 LOAD_GLOBAL              0 (l)
              4 GET_ITER
        >>    6 FOR_ITER                12 (to 20)
              8 STORE_FAST               0 (i)
    
             10 LOAD_GLOBAL              0 (l)
             12 LOAD_ATTR                1 (pop)
             14 CALL_FUNCTION            0
             16 POP_TOP
             18 JUMP_ABSOLUTE            6
    

    That is, it get's the iter for the given object (iter(l) a specialized iterator object for lists) and loops until FOR_ITER signals that it's time to stop. Adding the juicy parts, here's what FOR_ITER does:

    PyObject *next = (*iter->ob_type->tp_iternext)(iter);
    

    which essentially is:

    list_iterator.__next__()
    

    this (finally*) goes through to listiter_next which performs the index check as @Alex using the original sequence l during the check.

    if (it->it_index < PyList_GET_SIZE(seq))
    

    when this fails, NULL is returned which signals that the iteration has finished. In the meantime a StopIteration exception is set which is silently suppressed in the FOR_ITER op-code code:

    if (!PyErr_ExceptionMatches(PyExc_StopIteration))
        goto error;
    else if (tstate->c_tracefunc != NULL)
        call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
    PyErr_Clear();  /* My comment: Suppress it! */
    

    so whether you change the list or not, the check in listiter_next will ultimately fail and do the same thing.

    *For anyone wondering, listiter_next is a descriptor so there's a little function wrapping it. In this specific case, that function is wrap_next which makes sure to set PyExc_StopIteration as an exception when listiter_next returns NULL.

提交回复
热议问题