Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/41.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python itertools中最长的izip_:迭代器中的rasing Indexer是如何工作的?_Python_Iterator_Itertools - Fatal编程技术网

Python itertools中最长的izip_:迭代器中的rasing Indexer是如何工作的?

Python itertools中最长的izip_:迭代器中的rasing Indexer是如何工作的?,python,iterator,itertools,Python,Iterator,Itertools,在问题@lazyr中,他询问了以下izip_longest迭代器的代码是如何工作的: 当我试图理解它是如何工作的时候,我偶然发现了一个问题: “如果indexer在作为参数发送到izip\u longest的迭代器中的一个迭代器中引发,该怎么办?” 然后我编写了一些测试代码: from itertools import izip_longest, repeat, chain, izip def izip_longest_from_docs(*args, **kwds): # The c

在问题@lazyr中,他询问了以下
izip_longest
迭代器的代码是如何工作的:

当我试图理解它是如何工作的时候,我偶然发现了一个问题: “如果
indexer
在作为参数发送到
izip\u longest
的迭代器中的一个迭代器中引发,该怎么办?”

然后我编写了一些测试代码:

from itertools import izip_longest, repeat, chain, izip

def izip_longest_from_docs(*args, **kwds):
    # The code is exactly the same as shown above
    ....

def gen1():
    for i in range(5):
        yield i

def gen2():
    for i in range(10):
        if i==8:
            raise IndexError #simulation IndexError raised inside the iterator
        yield i

for i in izip_longest_from_docs(gen1(),gen2(), fillvalue = '-'):
    print('{i[0]} {i[1]}'.format(**locals()))

print('\n')

for i in izip_longest(gen1(),gen2(), fillvalue = '-'):
    print('{i[0]} {i[1]}'.format(**locals()))
结果证明,
itertools
模块中的函数和来自文档的
izip_longest_的函数工作方式不同

上述代码的输出:

>>> 
0 0
1 1
2 2
3 3
4 4
- 5
- 6
- 7


0 0
1 1
2 2
3 3
4 4
- 5
- 6
- 7

Traceback (most recent call last):
  File "C:/..., line 31, in <module>
    for i in izip_longest(gen1(),gen2(), fillvalue = '-'):
  File "C:/... test_IndexError_inside iterator.py", line 23, in gen2
    raise IndexError
IndexError
>
0 0
1 1
2 2
3 3
4 4
- 5
- 6
- 7
0 0
1 1
2 2
3 3
4 4
- 5
- 6
- 7
回溯(最近一次呼叫最后一次):
文件“C:/…,第31行,在
对于izip_中最长的i(gen1(),gen2(),fillvalue='-'):
文件“C:/。。。测试迭代器.py中的索引器”,第23行,第2代
提升索引器
索引器
因此,可以清楚地看到,
itertools
中的
izip_longes
代码确实传播了
indexer
异常(我认为应该是这样),但是
izip_longes_从
文档中“吞没”了
indexer
异常,因为它将其视为停止迭代的信号


我的问题是,他们是如何解决
itertools
模块中的
indexer
传播问题的?

izip\u longest\u next
中,没有使用哨兵

相反,CPython通过计数器跟踪有多少迭代器仍然处于活动状态,并在活动数量达到零时停止

如果发生错误,它将结束迭代,就像没有迭代器仍然处于活动状态一样,并允许错误传播

守则:

            item = PyIter_Next(it);
            if (item == NULL) {
                lz->numactive -= 1;
                if (lz->numactive == 0 || PyErr_Occurred()) {
                    lz->numactive = 0;
                    Py_DECREF(result);
                    return NULL;
                } else {
                    Py_INCREF(lz->fillvalue);
                    item = lz->fillvalue;
                    PyTuple_SET_ITEM(lz->ittuple, i, NULL);
                    Py_DECREF(it);
                }
            }
我看到的最简单的解决方案是:

def izip_longest_modified(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    class LongestExhausted(Exception):
        pass
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
        try:
            yield counter()         # yields the fillvalue, or raises IndexError
        except:
            raise LongestExhausted
    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except LongestExhausted:
        pass

@agf我想要,但我找不到。谢谢你的链接!我会看一看。@agf的C:o格式对我来说似乎有点太复杂了。然而,所谓的等价物在功能(而不是速度)上似乎并不是绝对等价的。是的,你是对的。”如果您在本文档中发现错误或希望提出改进建议,请发送电子邮件至docs@python.org描述错误以及在哪里发现的。如果你有一个如何修复它的建议,也包括在内。“如果你不喜欢,我会做的?@agf,请做吧。我没有信心提出任何更正。@agf我现在能想到的唯一解决方案是修改
sentinel
,它将实现一些计数器,并在需要时提出自己的特殊异常。谢谢!不过,我必须补充一点,在我看来,所谓的纯Python等价物的功能与函数本身稍有不同并不太好。奇怪的是,他们为什么这么说。无论如何,理解带有哨兵的代码对大脑来说是一项很好的任务。@ovgo再试一次吗?我在第一次发布后编辑了它。这似乎对我有用。是的,有用!当我遇到您提供的第一个版本不起作用时,我开始思考并产生了以下代码def sentinel(fillvalue=fillvalue,counter=[0]):def ret():counter[0]+=1 if counter[0]==len(args):raise LONGESTEXAUSTED YOULT fillvalue ret ret()Oops。似乎如此,所以在评论中不保留缩进。我会尽力解释的。我添加了counter=[0],以便与
sentinel
返回的所有迭代器共享该值(因为
list
是可变的)。不过,在参数列表中使用counter=[0]是否可以。但是如果
sentinel
,我就不能被放入体内,因为[0]会被创建,并且在
sentinel
被调用时再次被创建。我没有把它放在
sentinel
函数的正上方,因为它会导致作用域问题,而且
global
关键字似乎不能解决这个问题。@ovgo Look up。函数参数默认值在定义时计算,因此每次运行函数时都是相同的。这只对可变默认值重要。在Python3上,可以使用“nonlocal”关键字将
counter
置于
sentinal
之外。
def izip_longest_modified(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    class LongestExhausted(Exception):
        pass
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
        try:
            yield counter()         # yields the fillvalue, or raises IndexError
        except:
            raise LongestExhausted
    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except LongestExhausted:
        pass