如何优雅地使用Python迭代器

发布于 2024-10-07 03:31:00 字数 707 浏览 2 评论 0原文

我尝试更多地使用迭代器进行循环,因为我听说它比索引循环更快。我不确定的一件事是如何很好地处理序列的结尾。我能想到的方法是使用 tryexcept StopIteration,这对我来说看起来很难看。

更具体地说,假设我们被要求打印两个排序列表 ab 的合并排序列表。我会写以下内容

aNull = False
I = iter(a)
try:
    tmp = I.next()
except StopIteration:
    aNull = True

for x in b:
    if aNull:
        print x
    else:
        if x < tmp:
            print x
        else:
            print tmp,x
            try:
                tmp = I.next()
            except StopIteration:
                aNull = True

while not aNull:
    print tmp
    try:
        tmp = I.next()
    except StopIteration:
        aNull = True

您将如何编码以使其更整洁?

I am trying to use iterators more for looping since I heard it is faster than index looping. One thing I am not sure is about how to treat the end of the sequence nicely. The way I can think of is to use try and except StopIteration, which looks ugly to me.

To be more concrete, suppose we are asked to print the merged sorted list of two sorted lists a and b. I would write the following

aNull = False
I = iter(a)
try:
    tmp = I.next()
except StopIteration:
    aNull = True

for x in b:
    if aNull:
        print x
    else:
        if x < tmp:
            print x
        else:
            print tmp,x
            try:
                tmp = I.next()
            except StopIteration:
                aNull = True

while not aNull:
    print tmp
    try:
        tmp = I.next()
    except StopIteration:
        aNull = True

How would you code it to make it neater?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

掩于岁月 2024-10-14 03:31:00

我认为更对称地处理 ab 会让它更容易阅读。此外,在 Python 2.6 中使用带有默认值的内置 next 函数可以避免处理 StopIteration 的需要:

def merge(a, b):
    """Merges two iterators a and b, returning a single iterator that yields
    the elements of a and b in non-decreasing order.  a and b are assumed to each
    yield their elements in non-decreasing order."""

    done = object()
    aNext = next(a, done)
    bNext = next(b, done)

    while (aNext is not done) or (bNext is not done):
        if (bNext is done) or ((aNext is not done) and (aNext < bNext)):
            yield aNext
            aNext = next(a, done)
        else:
            yield bNext
            bNext = next(b, done)

for i in merge(iter(a), iter(b)):
    print i

以下函数概括了适用于任意多个迭代器的方法。

def merge(*iterators):
    """Merges a collection of iterators, returning a single iterator that yields
    the elements of the original iterators in non-decreasing order.  Each of
    the original iterators is assumed to yield its elements in non-decreasing
    order."""

    done = object()
    n = [next(it, done) for it in iterators]

    while any(v is not done for v in n):
        v, i = min((v, i) for (i, v) in enumerate(n) if v is not done)
        yield v
        n[i] = next(iterators[i], done)

I think handling a and b more symmetrically would make it easier to read. Also, using the built-in next function in Python 2.6 with a default value avoids the need to handle StopIteration:

def merge(a, b):
    """Merges two iterators a and b, returning a single iterator that yields
    the elements of a and b in non-decreasing order.  a and b are assumed to each
    yield their elements in non-decreasing order."""

    done = object()
    aNext = next(a, done)
    bNext = next(b, done)

    while (aNext is not done) or (bNext is not done):
        if (bNext is done) or ((aNext is not done) and (aNext < bNext)):
            yield aNext
            aNext = next(a, done)
        else:
            yield bNext
            bNext = next(b, done)

for i in merge(iter(a), iter(b)):
    print i

The following function generalizes the approach to work for arbitrarily many iterators.

def merge(*iterators):
    """Merges a collection of iterators, returning a single iterator that yields
    the elements of the original iterators in non-decreasing order.  Each of
    the original iterators is assumed to yield its elements in non-decreasing
    order."""

    done = object()
    n = [next(it, done) for it in iterators]

    while any(v is not done for v in n):
        v, i = min((v, i) for (i, v) in enumerate(n) if v is not done)
        yield v
        n[i] = next(iterators[i], done)
末骤雨初歇 2024-10-14 03:31:00

您错过了迭代器的全部要点。您无需手动调用 I.next(),只需迭代 I

for tmp in I:
    print tmp

已编辑

要合并两个迭代器,请使用 itertools 模块中非常方便的函数。您想要的可能是 izip

merged = []
for x, y in itertools.izip(a, b):
    if x < y:
        merged.append(x)
        merged.append(y)
    else:
        merged.append(y)
        merged.append(x)

再次编辑

正如评论中所指出的,这实际上不起作用,因为列表 a 中可能有多个项目小于列表 b 中的下一个项目。但是,我意识到还有另一个内置函数可以处理此问题: heapq.merge

You're missing the whole point of iterators. You don't manually call I.next(), you just iterate through I.

for tmp in I:
    print tmp

Edited

To merge two iterators, use the very handy functions in the itertools module. The one you want is probably izip:

merged = []
for x, y in itertools.izip(a, b):
    if x < y:
        merged.append(x)
        merged.append(y)
    else:
        merged.append(y)
        merged.append(x)

Edit again

As pointed out in the comments, this won't actually work, because there could be multiple items from list a smaller than the next item in list b. However, I realised that there is another built-in funciton that deals with this: heapq.merge.

明天过后 2024-10-14 03:31:00

函数sorted适用于列表和迭代器。也许这不是您想要的,但下面的代码可以工作。


a.expand(b)
print sorted(iter(a))

The function sorted works with lists and iterators. Maybe it is not what you desire, but the following code works.


a.expand(b)
print sorted(iter(a))

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文