Commit da1734c5 authored by Cheryl Sabella's avatar Cheryl Sabella Committed by Raymond Hettinger

bpo-27212: Modify islice recipe to consume initial values preceding start (GH-6195)

parent 83494037
...@@ -436,15 +436,24 @@ loops that truncate the stream. ...@@ -436,15 +436,24 @@ loops that truncate the stream.
# islice('ABCDEFG', 2, None) --> C D E F G # islice('ABCDEFG', 2, None) --> C D E F G
# islice('ABCDEFG', 0, None, 2) --> A C E G # islice('ABCDEFG', 0, None, 2) --> A C E G
s = slice(*args) s = slice(*args)
it = iter(range(s.start or 0, s.stop or sys.maxsize, s.step or 1)) start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1
it = iter(range(start, stop, step))
try: try:
nexti = next(it) nexti = next(it)
except StopIteration: except StopIteration:
# Consume *iterable* up to the *start* position.
for i, element in zip(range(start), iterable):
pass
return return
try:
for i, element in enumerate(iterable): for i, element in enumerate(iterable):
if i == nexti: if i == nexti:
yield element yield element
nexti = next(it) nexti = next(it)
except StopIteration:
# Consume to *stop*.
for i, element in zip(range(i + 1, stop), iterable):
pass
If *start* is ``None``, then iteration starts at zero. If *step* is ``None``, If *start* is ``None``, then iteration starts at zero. If *step* is ``None``,
then the step defaults to one. then the step defaults to one.
...@@ -688,8 +697,8 @@ which incur interpreter overhead. ...@@ -688,8 +697,8 @@ which incur interpreter overhead.
# tail(3, 'ABCDEFG') --> E F G # tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n)) return iter(collections.deque(iterable, maxlen=n))
def consume(iterator, n): def consume(iterator, n=None):
"Advance the iterator n-steps ahead. If n is none, consume entirely." "Advance the iterator n-steps ahead. If n is None, consume entirely."
# Use functions that consume iterators at C speed. # Use functions that consume iterators at C speed.
if n is None: if n is None:
# feed the entire iterator into a zero-length deque # feed the entire iterator into a zero-length deque
......
...@@ -1192,6 +1192,7 @@ class TestBasicOps(unittest.TestCase): ...@@ -1192,6 +1192,7 @@ class TestBasicOps(unittest.TestCase):
(10, 20, 3), (10, 20, 3),
(10, 3, 20), (10, 3, 20),
(10, 20), (10, 20),
(10, 10),
(10, 3), (10, 3),
(20,) (20,)
]: ]:
...@@ -1218,6 +1219,10 @@ class TestBasicOps(unittest.TestCase): ...@@ -1218,6 +1219,10 @@ class TestBasicOps(unittest.TestCase):
self.assertEqual(list(islice(it, 3)), list(range(3))) self.assertEqual(list(islice(it, 3)), list(range(3)))
self.assertEqual(list(it), list(range(3, 10))) self.assertEqual(list(it), list(range(3, 10)))
it = iter(range(10))
self.assertEqual(list(islice(it, 3, 3)), [])
self.assertEqual(list(it), list(range(3, 10)))
# Test invalid arguments # Test invalid arguments
ra = range(10) ra = range(10)
self.assertRaises(TypeError, islice, ra) self.assertRaises(TypeError, islice, ra)
...@@ -1604,6 +1609,48 @@ class TestExamples(unittest.TestCase): ...@@ -1604,6 +1609,48 @@ class TestExamples(unittest.TestCase):
self.assertEqual(list(takewhile(lambda x: x<5, [1,4,6,4,1])), [1,4]) self.assertEqual(list(takewhile(lambda x: x<5, [1,4,6,4,1])), [1,4])
class TestPurePythonRoughEquivalents(unittest.TestCase):
@staticmethod
def islice(iterable, *args):
s = slice(*args)
start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1
it = iter(range(start, stop, step))
try:
nexti = next(it)
except StopIteration:
# Consume *iterable* up to the *start* position.
for i, element in zip(range(start), iterable):
pass
return
try:
for i, element in enumerate(iterable):
if i == nexti:
yield element
nexti = next(it)
except StopIteration:
# Consume to *stop*.
for i, element in zip(range(i + 1, stop), iterable):
pass
def test_islice_recipe(self):
self.assertEqual(list(self.islice('ABCDEFG', 2)), list('AB'))
self.assertEqual(list(self.islice('ABCDEFG', 2, 4)), list('CD'))
self.assertEqual(list(self.islice('ABCDEFG', 2, None)), list('CDEFG'))
self.assertEqual(list(self.islice('ABCDEFG', 0, None, 2)), list('ACEG'))
# Test items consumed.
it = iter(range(10))
self.assertEqual(list(self.islice(it, 3)), list(range(3)))
self.assertEqual(list(it), list(range(3, 10)))
it = iter(range(10))
self.assertEqual(list(self.islice(it, 3, 3)), [])
self.assertEqual(list(it), list(range(3, 10)))
# Test that slice finishes in predictable state.
c = count()
self.assertEqual(list(self.islice(c, 1, 3, 50)), [1])
self.assertEqual(next(c), 3)
class TestGC(unittest.TestCase): class TestGC(unittest.TestCase):
def makecycle(self, iterator, container): def makecycle(self, iterator, container):
...@@ -2158,6 +2205,17 @@ Samuele ...@@ -2158,6 +2205,17 @@ Samuele
... "Return function(0), function(1), ..." ... "Return function(0), function(1), ..."
... return map(function, count(start)) ... return map(function, count(start))
>>> import collections
>>> def consume(iterator, n=None):
... "Advance the iterator n-steps ahead. If n is None, consume entirely."
... # Use functions that consume iterators at C speed.
... if n is None:
... # feed the entire iterator into a zero-length deque
... collections.deque(iterator, maxlen=0)
... else:
... # advance to the empty slice starting at position n
... next(islice(iterator, n, n), None)
>>> def nth(iterable, n, default=None): >>> def nth(iterable, n, default=None):
... "Returns the nth item or a default value" ... "Returns the nth item or a default value"
... return next(islice(iterable, n, None), default) ... return next(islice(iterable, n, None), default)
...@@ -2298,6 +2356,14 @@ perform as purported. ...@@ -2298,6 +2356,14 @@ perform as purported.
>>> list(islice(tabulate(lambda x: 2*x), 4)) >>> list(islice(tabulate(lambda x: 2*x), 4))
[0, 2, 4, 6] [0, 2, 4, 6]
>>> it = iter(range(10))
>>> consume(it, 3)
>>> next(it)
3
>>> consume(it)
>>> next(it, 'Done')
'Done'
>>> nth('abcde', 3) >>> nth('abcde', 3)
'd' 'd'
...@@ -2386,6 +2452,7 @@ def test_main(verbose=None): ...@@ -2386,6 +2452,7 @@ def test_main(verbose=None):
test_classes = (TestBasicOps, TestVariousIteratorArgs, TestGC, test_classes = (TestBasicOps, TestVariousIteratorArgs, TestGC,
RegressionTests, LengthTransparency, RegressionTests, LengthTransparency,
SubclassWithKwargsTest, TestExamples, SubclassWithKwargsTest, TestExamples,
TestPurePythonRoughEquivalents,
SizeofTest) SizeofTest)
support.run_unittest(*test_classes) support.run_unittest(*test_classes)
......
Modify documentation for the :func:`islice` recipe to consume initial values
up to the start index.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment