オブジェクトは簿記を処理し、オブジェクトはkey
と親groupby
オブジェクトを参照するだけです。
typedef struct {
PyObject_HEAD
PyObject *it; /* iterator over the input sequence */
PyObject *keyfunc; /* the second argument for the groupby function */
PyObject *tgtkey; /* the key for the current "grouper" */
PyObject *currkey; /* the key for the current "item" of the iterator*/
PyObject *currvalue; /* the plain value of the current "item" */
} groupbyobject;
typedef struct {
PyObject_HEAD
PyObject *parent; /* the groupby object */
PyObject *tgtkey; /* the key value for this grouper object. */
} _grouperobject;
groupby
オブジェクトをアンパックするときにgrouper
オブジェクトを反復しているわけではないので、今は無視します。それでは、興味深いのは、あなたがそれにnext
を呼び出したときgroupby
で何が起こるかです:
static PyObject *
groupby_next(groupbyobject *gbo)
{
PyObject *newvalue, *newkey, *r, *grouper;
/* skip to next iteration group */
for (;;) {
if (gbo->currkey == NULL)
/* pass */;
else if (gbo->tgtkey == NULL)
break;
else {
int rcmp;
rcmp = PyObject_RichCompareBool(gbo->tgtkey, gbo->currkey, Py_EQ);
if (rcmp == 0)
break;
}
newvalue = PyIter_Next(gbo->it);
if (newvalue == NULL)
return NULL; /* just return NULL, no invalidation of attributes */
newkey = PyObject_CallFunctionObjArgs(gbo->keyfunc, newvalue, NULL);
gbo->currkey = newkey;
gbo->currvalue = newvalue;
}
gbo->tgtkey = gbo->currkey;
grouper = _grouper_create(gbo, gbo->tgtkey);
r = PyTuple_Pack(2, gbo->currkey, grouper);
return r;
}
は、私はすべての無関係な例外処理コードおよび削除または簡略化された純粋な参照カウントのものを削除しました。ここで興味深いのは、イテレータの最後に到達したときにgbo->currkey
,gbo->currvalue
およびgbo->tgtkey
がNULL
に設定されていない場合は、PyIter_Next(gbo->it) == NULL
の場合はちょうどreturn NULL
なので、最後に検出された値(イテレータの最後のアイテム) 。
これが完了したら、2つのgrouper
オブジェクトがあります。最初のものはtgtvalue
がFalse
で、2番目がTrue
です。
static PyObject *
_grouper_next(_grouperobject *igo)
{
groupbyobject *gbo = (groupbyobject *)igo->parent;
PyObject *newvalue, *newkey, *r;
int rcmp;
if (gbo->currvalue == NULL) {
/* removed because irrelevant. */
}
rcmp = PyObject_RichCompareBool(igo->tgtkey, gbo->currkey, Py_EQ);
if (rcmp <= 0)
/* got any error or current group is end */
return NULL;
r = gbo->currvalue; /* this accesses the last value of the groupby object */
gbo->currvalue = NULL;
gbo->currkey = NULL;
return r;
}
のでcurrvalue
がNULL
ではないので、最初のif
分岐は興味深いものではありません覚えている:あなたはこれらのgrouper
の上next
を呼び出すときに何が起こるか見てみましょう。あなたの最初のグルーパーについては、grouper
とgroupby
オブジェクトのtgtkey
を比較し、それらが異なっていると見て、それはすぐにreturn NULL
になります。だからあなたは空リストを持っています。 tgtkey
Sが同一であるので、それは、groupby
オブジェクトのcurrvalue
戻ります(イテレータの最後の遭遇値である!)が、今回はそれがcurrvalue
とcurrkey
のを設定します第二イテレータについては
groupby
のオブジェクトはNULL
です。
バックのpythonに切り替え
:あなたのgroupby
で最後のグループと同じtgtkey
とgrouper
を持っている場合は本当に面白い癖が起こる:
import itertools
>>> inputs = [(x > 5, x) for x in range(10)] + [(False, 10)]
>>> (_, g1), (_, g2), (_, g3) = itertools.groupby(inputs, key=lambda x: x[0])
>>> list(g1)
[(False, 10)]
>>> list(g3)
[]
g1
内の1つの要素が属していなかったこと最初のグルーパーオブジェクトのtgtkey
がFalse
であり、最後のtgtkey
がFalse
であるため、最初のグループはそれが最初のグループに属すると考えました。また、groupby
オブジェクトを無効にして、3番目のグループが空になるようにしました。
すべてのコードはthe Python source codeから取得されていますが、短縮されています。
PyPyをCPythonのように変更しました。私はこれが本当に「なぜ」という質問に本当に答えるものではないことを知っています.CPythonからPyPyに正確なアルゴリズムをコピーするだけで、ドキュメントから再構築されたバージョンを使用するのではなく、同じ結果が得られます。 –
動作は少し壊れているようです。 CPythonの問題報告でこの質問をしたいと思うかもしれません。おそらく、それは次の3.7リリースで修正されるでしょう。あるいは、現在の行動がより良い理由が誰かに見つかるかもしれません。またはあなたの問題は永遠に開いたままになります。 –
興味のある人は、[このコミット](https://bitbucket.org/pypy/pypy/commits/6093ff1a44e6b17f09db83aa80aea562a738c286)でpypyの変更が行われたようです。 – mgilson