簡體   English   中英

從itertools.cycle中提取列表

[英]Extract a list from itertools.cycle

我有一個包含itertools.cycle實例的類,我希望能夠復制它。 一種方法(我能想出的唯一方法)是提取初始可迭代(這是一個列表),並存儲循環所處的位置。

不幸的是我無法掌握我用來創建循環實例的列表,似乎也沒有明顯的方法來做到這一點:

import itertools
c = itertools.cycle([1, 2, 3])
print dir(c)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', 
 '__hash__', '__init__', '__iter__', '__new__', '__reduce__', 
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', 
 '__subclasshook__', 'next']

我可以提出一些合理的理由,為什么對某些類型的輸入迭代不允許這樣做,但是對於一個元組甚至一個列表(可變性可能是一個問題),我不明白為什么它不會可能。

任何人都知道是否可以從itertools.cycle實例中提取非無限可迭代。 如果沒有,任何人都知道為什么這個想法很糟糕?

不可能。 如果你查看itertools.cycle代碼,你會發現它沒有存儲序列的副本。 它只創建一個iterable並將迭代中包含的值存儲在新創建的列表中:

static PyObject *
cycle_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyObject *it;
    PyObject *iterable;
    PyObject *saved;
    cycleobject *lz;

    if (type == &cycle_type && !_PyArg_NoKeywords("cycle()", kwds))
        return NULL;

    if (!PyArg_UnpackTuple(args, "cycle", 1, 1, &iterable))
        return NULL;
    /* NOTE: they do not store the *sequence*, only the iterator */
    /* Get iterator. */
    it = PyObject_GetIter(iterable);
    if (it == NULL)
        return NULL;

    saved = PyList_New(0);
    if (saved == NULL) {
        Py_DECREF(it);
        return NULL;
    }

    /* create cycleobject structure */
    lz = (cycleobject *)type->tp_alloc(type, 0);
    if (lz == NULL) {
        Py_DECREF(it);
        Py_DECREF(saved);
        return NULL;
    }
    lz->it = it;
    lz->saved = saved;
    lz->firstpass = 0;

    return (PyObject *)lz;
}

這意味着在做的時候:

itertools.cycle([1,2,3])

您創建的列表只有一個引用,它保存在循環使用的迭代器中。 當迭代器耗盡時,迭代器將被刪除並創建一個新的迭代器:

    /* taken from the "cycle.next" implementation */
    it = PyObject_GetIter(lz->saved);
    if (it == NULL)
        return NULL;
    tmp = lz->it;
    lz->it = it;
    lz->firstpass = 1;
    Py_DECREF(tmp);   /* destroys the old iterator */

這意味着在執行一個循環后,列表將被銷毀。

無論如何,如果你需要訪問這個列表,只需在調用itertools.cycle之前在某處引用它。

如果你有辦法知道cycle產生的對象的某些屬性,那么你可以推導出內部列表。 例如,如果您知道循環中的所有對象都是不同的並且除了您之外沒有其他任何東西從cycle迭代器讀取,那么您可以簡單地等待您看到的第一個再次出現(使用is == )進行測試終止內部列表。

但是,如果沒有這些知識,就沒有任何保證,而且在某些情況下,您選擇猜測循環的任何方法都會失敗。

好的,所以我接受了@ Bakuriu的回答,因為它在技術上是正確的。 無法復制/ pickle itertools.cycle對象。

我已經實現了一個itertools.cycle的子類,它可選擇的(帶有幾個額外的鈴聲和口哨來啟動)。

import itertools


class FiniteCycle(itertools.cycle):
    """
    Cycles the given finite iterable indefinitely. 
    Subclasses ``itertools.cycle`` and adds pickle support.
    """
    def __init__(self, finite_iterable):
        self._index = 0
        self._iterable = tuple(finite_iterable)
        self._iterable_len = len(self._iterable)
        itertools.cycle.__init__(self, self._iterable)

    @property
    def index(self):
        return self._index

    @index.setter
    def index(self, index):
        """
        Sets the current index into the iterable. 
        Keeps the underlying cycle in sync.

        Negative indexing supported (will be converted to a positive index).
        """
        index = int(index)
        if index < 0:
            index = self._iterable_len + index
            if index < 0:
                raise ValueError('Negative index is larger than the iterable length.')

        if index > self._iterable_len - 1:
            raise IndexError('Index is too high for the iterable. Tried %s, iterable '
                             'length %s.' % (index, self._iterable_len))

        # calculate the positive number of times the iterable will need to be moved
        # forward to get to the desired index
        delta = (index + self._iterable_len - self.index) % (self._iterable_len)

        # move the finite cycle on ``delta`` times.
        for _ in xrange(delta):
            self.next()

    def next(self):
        self._index += 1
        if self._index >= self._iterable_len:
            self._index = 0
        return itertools.cycle.next(self)

    def peek(self):
        """
        Return the next value in the cycle without moving the iterable forward.
        """
        return self._iterable[self.index]

    def __reduce__(self):
        return (FiniteCycle, (self._iterable, ), {'index': self.index})

    def __setstate__(self, state):
        self.index = state.pop('index')

一些示例用法:

c = FiniteCycle([1, 2, 3])

c.index = -1
print c.next() # prints 3

print [c.next() for _ in xrange(4)] # prints [1, 2, 3, 1]

print c.peek() # prints 2
print c.next() # prints 2

import pickle
import cStringIO
serialised_cycle = pickle.dumps(c)

del c

c = pickle.loads(serialised_cycle)

print c.next() # prints 3
print c.next() # prints 1

歡迎反饋。

謝謝,

根據您使用cycle ,您甚至可以使用自定義類包裝器,如下所示:

class SmartCycle:
    def __init__(self, x):
        self.cycle = cycle(x)
        self.to_list = x

    def __next__(self):
        return next(self.cycle)

例如

> a = SmartCycle([1, 2, 3])
> for _ in range(4):
>     print(next(a))
1
2
3
1

> a.to_list
[1, 2, 3]

暫無
暫無

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

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