簡體   English   中英

在Python 2.7.6中列出和迭代迭代器之間的區別

[英]Difference between listing and iterating over an iterator in Python 2.7.6

我最近一直在玩一些涉及迭代器的代碼:

"""IntegerPartitions.py

Generate and manipulate partitions of integers into sums of integers.

D. Eppstein, August 2005.

https://www.ics.uci.edu/~eppstein/PADS/IntegerPartitions.py
"""

def mckay(n):
    """
    Integer partitions of n, in reverse lexicographic order.
    The output and asymptotic runtime are the same as mckay(n),
    but the algorithm is different: it involves no division,
    and is simpler than mckay, but uses O(n) extra space for
    a recursive call stack.
    """
    if n == 0:
        yield []
    if n <= 0:
        return
    for p in mckay(n-1):
        if len(p) == 1 or (len(p) > 1 and p[-1] < p[-2]):
            p[-1] += 1
            yield p
            p[-1] -= 1
        p.append(1)
        yield p
        p.pop()

該程序接受一個整數,並返回一個生成器,該生成器輸出該整數的分區。

但是,當我嘗試在代碼中使用它時,我注意到了一些奇怪的東西。

>>> p = mckay(4)
>>> print list(p)
[[], [], [], [], []]
>>> q = mckay(4)
>>> cumulator = []
>>> for x in q :
...     cumulator.append(x)
>>> print cumulator
[[], [], [], [], []]
>>> print list(mckay(4))
[[], [], [], [], []]
>>> r = mckay(4)
>>> for x in r :
...     print x
[4]
[3, 1]
[2, 2]
[2, 1, 1]
[1, 1, 1, 1]
>>> for x in mckay(4) :
...     print x
[4]
[3, 1]
[2, 2]
[2, 1, 1]
[1, 1, 1, 1]

除非我一張一張地打印,否則分區似乎不會顯示。 這是該語言的錯誤嗎(我的版本是Ubuntu Trusty上的Python 2.7.6),還是我缺少某些東西? 我瀏覽過Google,但似乎找不到任何與此相關的信息。

我認為這可能與遞歸調用有關,但是我使用以下代碼嘗試了一下,並發現了相似的結果

def mckay(n):
    """
    Integer partitions of n, in reverse lexicographic order.
    Note that the generated output consists of the same list object,
    repeated the correct number of times; the caller must leave this
    list unchanged, and must make a copy of any partition that is
    intended to last longer than the next call into the generator.
    The algorithm follows Knuth v4 fasc3 p38 in rough outline.
    """
    if n == 0:
        yield []
    if n <= 0:
        return
    partition = [n]
    last_nonunit = (n > 1) - 1
    while True:
        yield partition
        if last_nonunit < 0:
            return
        if partition[last_nonunit] == 2:
            partition[last_nonunit] = 1
            partition.append(1)
            last_nonunit -= 1
            continue
        replacement = partition[last_nonunit] - 1
        total_replaced = replacement + len(partition) - last_nonunit
        reps,rest = divmod(total_replaced,replacement)
        partition[last_nonunit:] = reps*[replacement]
        if rest:
            partition.append(rest)
        last_nonunit = len(partition) - (partition[-1]==1) - 1

結果幾乎相同:

>>> p = mckay(4)
>>> print list(p)
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> q = mckay(4)
>>> cumulator = []
>>> for x in q :
...     cumulator.append(x)
>>> print cumulator
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> print list(mckay(4))
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> r = mckay(4)
>>> for x in r :
...     print x
[4]
[3, 1]
[2, 2]
[2, 1, 1]
[1, 1, 1, 1]
>>> for x in mckay(4) :
...     print x
[4]
[3, 1]
[2, 2]
[2, 1, 1]
[1, 1, 1, 1]

問題在於mckay函數正在修改同一個列表對象,因此,當您在其上調用list()時,實際上會得到一個包含4個實際上指向同一對象的項的列表。 因此,最后,列表對象為空,您所獲得的只是帶有空列表的列表。

>>> p = mckay(4)
>>> [id(x) for x in p]
[139854369904832, 139854369904832, 139854369904832, 139854369904832, 139854369904832]

>>> for x in mckay(4):
    print x, '-->', id(x)

[4] --> 140446845125552
[3, 1] --> 140446845125552
[2, 2] --> 140446845125552
[2, 1, 1] --> 140446845125552
[1, 1, 1, 1] --> 140446845125552
>>> x # The actual list object is empty at the end of the iteration
[]
>>> id(x)
140446845125552

但是,當您遍歷它時,您只是在立即打印返回的對象,從而得到不同的輸出,這里的解決方法是產生淺表副本:

yield p[:]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM