itertools.groupby
tài liệu nói rằng
itertools.groupby(iterable, key=None)
[...]
Hoạt động của groupby()
tương tự như bộ lọc uniq trong Unix. Nó tạo ra một break hoặc nhóm mới mỗi khi giá trị của các chức năng chính thay đổi (đó là lý do tại sao nó thường là cần thiết để có được sắp xếp dữ liệu bằng cách sử dụng cùng một chức năng quan trọng). Hành vi đó khác với GROUP BY của SQL, tập hợp các phần tử phổ biến bất kể thứ tự đầu vào của chúng.
Nhóm được trả về tự là một trình lặp có chia sẻ lặp lại cơ bản với groupby()
. Vì nguồn được chia sẻ, khi đối tượng `groupby() được nâng cao, nhóm trước đó không còn nhìn thấy được nữa. Vì vậy, nếu dữ liệu là cần thiết sau, nó nên được lưu trữ như một danh sách [-]
Vì vậy, các giả định từ đoạn cuối cùng là rằng danh sách được tạo ra sẽ là danh sách trống []
, kể từ khi trình vòng lặp được nâng cao, và đã đáp ứng StopIteration
; nhưng thay vì trong CPython kết quả là đáng ngạc nhiên [9]
.
Điều này là do _grouper
iterator chậm một mục phía sau iterator gốc, đó là bởi vì groupby
nhu cầu để nhìn trộm một mục trước để xem nếu nó thuộc về hiện tại hoặc các nhóm tiếp theo, nhưng nó phải có khả năng sau tạo mục này làm mục đầu tiên của nhóm mới.
Tuy nhiên currkey
và currvalue
thuộc tính của groupby
là không reset khi original iterator is exhausted, vì vậy currvalue
vẫn trỏ tới mục cuối cùng từ iterator.
Các tài liệu CPython thực sự chứa mã này tương đương, mà còn có hành vi chính xác giống như mã số phiên bản 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)
Đáng chú ý là các __next__
tìm thấy mục đầu tiên của nhóm tiếp theo, và lưu trữ nó quan trọng của nó vào self.currkey
và giá trị của nó là self.currvalue
. Nhưng quan trọng là dòng
self.currvalue = next(self.it) # Exit on StopIteration
Khi next
ném StopItertion
các self.currvalue
vẫn chứa chìa khóa cuối cùng của nhóm trước. Bây giờ, khi y[1]
được tạo thành list
, đầu tiên mang lại giá trị self.currvalue
và chỉ sau đó chạy next()
trên trình lặp cơ bản (và đáp ứng lại StopIteration
).
Mặc dù có Python tương đương trong tài liệu, mà ứng xử chính xác như việc thực hiện mã C có thẩm quyền trong CPython, IronPython, Jython và PyPy cho kết quả khác nhau.