6

itertools.cycleコピーできるようにしたいインスタンスを含むクラスがあります。1つのアプローチ(私が思いつくことができる唯一のアプローチ)は、最初の反復可能ファイル(リストでした)を抽出し、サイクルが存在する位置を保存することです。

残念ながら、サイクルインスタンスを作成するために使用したリストを取得できません。また、それを行うための明白な方法がないようです。

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ます。そうでなければ、なぜこのアイデアが悪いアイデアなのか誰かが知っていますか?

4

4 に答える 4

5

それは不可能だ。コードを見るitertools.cycleと、シーケンスのコピーが保存されていないことがわかります。iterableを作成し、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])

作成するリストには参照が1つだけあり、それはcycleによって使用されるイテレーターに保持されます。イテレータが使い果たされると、イテレータは削除され、新しいイテレータが作成されます。

    /* 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 */

これは、1サイクルを実行した後、リストが破棄されることを意味します。

とにかく、このリストにアクセスする必要がある場合は、を呼び出す前にどこかで参照してくださいitertools.cycle

于 2012-08-20T18:53:11.070 に答える
0

生成されるオブジェクトの特定のプロパティを知る方法がある場合はcycle、内部リストを推測できます。たとえば、サイクル内のすべてのオブジェクトが別個であり、他に何もcycleイテレータから読み取っていないことがわかっている場合は、最初に表示されたオブジェクトが再び表示されるのを待つだけで(isnotでテスト==)、内部を終了できます。リスト。

しかし、そのような知識がなければ、保証はなく、サイクルが何であるかを推測するために選択した方法は、特定の場合に失敗します。

于 2012-08-20T19:18:47.543 に答える
0

わかりました。技術的に正しいので、@Bakuriuの回答を受け入れました。itertools.cycleオブジェクトをコピー/ピクルすることはできません。

itertools.cycleのサブクラスを実装しました。これpicklableです(起動するための追加のベルとホイッスルがいくつかあります)。

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

フィードバックを歓迎します。

ありがとう、

于 2012-08-22T11:11:18.220 に答える
0

使用方法によっては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]
于 2018-10-12T23:34:21.710 に答える