-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
itertools.islice() doesn't release reference to the source iterator when the slice is exhausted #65520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This issue results in redundant memory consumption for e.g. in this case: ================================================ from itertools import *
def test_islice():
for item in lookahead:
pass
for item in items:
pass
if __name__ == "__main__":
test_islice() ================================================ When one uses pure pythonic implementation of itertools.islice() (taken from docs), the issue goes away as well, since this implementation doesn't store redundant reference to source iterator. ================================================ def islice(iterable, *args):
s = slice(*args)
it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1))
nexti = next(it)
for i, element in enumerate(iterable):
if i == nexti:
yield element
nexti = next(it) ================================================ Attaching patch for this issue. Have to change '__reduce__()' method since now unpickling of exhausted 'islice()' object cannot return old source iterator. |
Added patch for 2.7 version (no need to change '__reduce__()' method since it's not implemented). |
The ref-counts in the islice_reduce code don't look to be correct at first glance. |
Hi Raymond, |
Haven't reviewed the patch, but you should definitely add a unit test for the bugfix. |
Hi Antoine, ================================================ a = [random.random() for i in range(10)]
before = sys.getrefcount(a)
b = islice(a, 5)
for i in b: pass
after = sys.getrefcount(a)
self.assertEqual(before, after) ================================================ Attaching "issue21321_2.7_e3217efa6edd_3.diff" and "issue21321_3.4_8c8315bac6a8_3.diff" patches with this test included in "Lib/test/test_itertools.py". |
Anton, the test is wrong: it is taking a reference to the iterable object (the list), not the iterator. To check the reference to the original iterator is released, something like this would work: >>> import itertools, weakref
>>> it = (x for x in (1, 2))
>>> wr = weakref.ref(it)
>>> it = itertools.islice(it, 1)
>>> wr() is None
False
>>> list(it)
[1]
>>> wr() is None # returns True with the patch, False without
True |
(note I haven't looked at the C part of the patch) |
Hi Antoine,
my test works for me. It can be either
>>> a = [1, 2, 3]
or
>>> a = iter([1, 2, 3])
, no matter: both objects will be +1 referenced after taking
>>> b = islice(a, 1)
.
My test failed without patch and passed with one. But your test is more straightforward, thanks. |
Thanks. Could you also add a test for the islice_reduce additions? Or is it already tested? |
Hi Antoine, |
For the record, checks such as:
are better written: self.assertIsNotNone(wr()) No need to upload a new patch, I'm gonna make the change while committing :-) |
New changeset b795105db23a by Antoine Pitrou in branch '3.4': New changeset a627b3e3c9c8 by Antoine Pitrou in branch 'default': |
Patch committed, thank you! |
Antoine, not sure about 2.7. The issue first arose for me at Python 2.7, so I would prefer "issue21321_2.7_e3217efa6edd_4.diff" patch be applied. |
New changeset 8ee76e1b5aa6 by Antoine Pitrou in branch '2.7': |
Ok, then I've committed to 2.7 too. Thank you very much for contributing! |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: