itertools.groupby
documentazione dice che
itertools.groupby(iterable, key=None)
[...]
L'operazione di groupby()
è simile al filtro uniq in Unix. Genera un'interruzione o un nuovo gruppo ogni volta che cambia il valore della funzione del tasto (che è il motivo per cui di solito è necessario aver ordinato i dati utilizzando la stessa funzione chiave). Questo comportamento differisce da GROUP BY di SQL che aggrega elementi comuni indipendentemente dal loro ordine di input.
Il gruppo restituito è esso stesso un iteratore che condivide il iterabile sottostante con groupby()
. Poiché la sorgente è condivisa, quando l'oggetto `groupby() è avanzato, il gruppo precedente non è più visibile. Quindi, se è necessario che i dati più tardi, deve essere conservata come un elenco [-]
Così il presupposto dall'ultimo comma è che che l'elenco generato sarebbe la lista vuota []
, da quando l'iteratore è già avanzato e ha incontrato StopIteration
; ma invece in CPython il risultato è sorprendente [9]
.
Questo perché la _grouper
iterator ritardo rispetto un elemento dietro l'iteratore originale, che è perché groupby
ha bisogno di sbirciare un elemento in anticipo per vedere se appartiene alla corrente o il prossimo gruppo, tuttavia devono essere in grado di seguito cede questo articolo come primo elemento del nuovo gruppo.
Tuttavia le currkey
e currvalue
attributi del groupby
sono non resettato quando il original iterator is exhausted, in modo currvalue
ancora punti per l'ultimo elemento del iteratore.
La documentazione CPython contiene in realtà il codice equivalente, che ha anche lo stesso comportamento esattamente come il codice della versione C:
class groupby:
# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
def __init__(self, iterable, key=None):
if key is None:
key = lambda x: x
self.keyfunc = key
self.it = iter(iterable)
self.tgtkey = self.currkey = self.currvalue = object()
def __iter__(self):
return self
def __next__(self):
while self.currkey == self.tgtkey:
self.currvalue = next(self.it) # Exit on StopIteration
self.currkey = self.keyfunc(self.currvalue)
self.tgtkey = self.currkey
return (self.currkey, self._grouper(self.tgtkey))
def _grouper(self, tgtkey):
while self.currkey == tgtkey:
yield self.currvalue
try:
self.currvalue = next(self.it)
except StopIteration:
return
self.currkey = self.keyfunc(self.currvalue)
In particolare il __next__
trova il primo elemento del gruppo successivo, e lo memorizza la chiave in self.currkey
e il suo valore a self.currvalue
. Ma la chiave è la linea
self.currvalue = next(self.it) # Exit on StopIteration
Quando next
tiri StopItertion
il self.currvalue
contiene ancora l'ultima chiave del gruppo precedente. Ora, quando y[1]
viene convertito in ,, il primo produce il valore di self.currvalue
e solo in seguito viene eseguito next()
sull'iteratore sottostante (e soddisfa nuovamente StopIteration
).
Anche se c'è Python equivalente nella documentazione, che si comporta esattamente come l'attuazione del codice C autorevole CPython, IronPython, Jython e PyPy dare risultati diversi.