8000 [2.7] bpo-27212: Modify islice recipe to consume initial values prece… · python/cpython@325191b · GitHub
[go: up one dir, main page]

Skip to content

Commit 325191b

Browse files
csabellarhettinger
authored andcommitted
[2.7] bpo-27212: Modify islice recipe to consume initial values preceding start (GH-6195) (GH-6339)
(cherry picked from commit da1734c)
1 parent 72f3e08 commit 325191b

File tree

3 files changed

+90
-9
lines changed

3 files changed

+90
-9
lines changed

Doc/library/itertools.rst

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -411,12 +411,24 @@ loops that truncate the stream.
411411
# islice('ABCDEFG', 2, None) --> C D E F G
412412
# islice('ABCDEFG', 0, None, 2) --> A C E G
413413
s = slice(*args)
414-
it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1))
415-
nexti = next(it)
416-
for i, element in enumerate(iterable):
417-
if i == nexti:
418-
yield element
419-
nexti = next(it)
414+
start, stop, step = s.start or 0, s.stop or sys.maxint, s.step or 1
415+
it = iter(xrange(start, stop, step)))
416+
try:
417+
nexti = next(it)
418+
except StopIteration:
419+
# Consume *iterable* up to the *start* position.
420+
for i, element in izip(xrange(start), iterable):
421+
pass
422+
return
423+
try:
424+
for i, element in enumerate(iterable):
425+
if i == nexti:
426+
yield element
427+
nexti = next(it)
428+
except StopIteration:
429+
# Consume to *stop*.
430+
for i, element in izip(xrange(i + 1, stop), iterable):
431+
pass
420432

421433
If *start* is ``None``, then iteration starts at zero. If *step* is ``None``,
422434
8000 then the step defaults to one.
@@ -681,8 +693,8 @@ which incur interpreter overhead.
681693
"Return function(0), function(1), ..."
682694
return imap(function, count(start))
683695

684-
def consume(iterator, n):
685-
"Advance the iterator n-steps ahead. If n is none, consume entirely."
696+
def consume(iterator, n=None):
697+
"Advance the iterator n-steps ahead. If n is None, consume entirely."
686698
# Use functions that consume iterators at C speed.
687699
if n is None:
688700
# feed the entire iterator into a zero-length deque

Lib/test/test_itertools.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ def test_islice(self):
802802
(10, 20, 3),
803803
(10, 3, 20),
804804
(10, 20),
805+
(10, 10),
805806
(10, 3),
806807
(20,)
807808
]:
@@ -826,6 +827,10 @@ def test_islice(self):
826827
self.assertEqual(list(islice(it, 3)), range(3))
827828
self.assertEqual(list(it), range(3, 10))
828829

830+
it = iter(range(10))
831+
self.assertEqual(list(islice(it, 3, 3)), [])
832+
self.assertEqual(list(it), range(3, 10))
833+
829834
# Test invalid arguments
830835
self.assertRaises(TypeError, islice, xrange(10))
831836
self.assertRaises(TypeError, islice, xrange(10), 1, 2, 3, 4)
@@ -1084,6 +1089,48 @@ def test_takewhile(self):
10841089
self.assertEqual(list(takewhile(lambda x: x<5, [1,4,6,4,1])), [1,4])
10851090

10861091

1092+
class TestPurePythonRoughEquivalents(unittest.TestCase):
1093+
1094+
@staticmethod
1095+
def islice(iterable, *args):
1096+
s = slice(*args)
1097+
start, stop, step = s.start or 0, s.stop or sys.maxint, s.step or 1
1098+
it = iter(xrange(start, stop, step))
1099+
try:
1100+
nexti = next(it)
1101+
except StopIteration:
1102+
# Consume *iterable* up to the *start* position.
1103+
for i, element in izip(xrange(start), iterable):
1104+
pass
1105+
return
1106+
try:
1107+
for i, element in enumerate(iterable):
1108+
if i == nexti:
1109+
yield element
1110+
nexti = next(it)
1111+
except StopIteration:
1112+
# Consume to *stop*.
1113+
for i, element in izip(xrange(i + 1, stop), iterable):
1114+
pass
1115+
1116+
def test_islice_recipe(self):
1117+
self.assertEqual(list(self.islice('ABCDEFG', 2)), list('AB'))
1118+
self.assertEqual(list(self.islice('ABCDEFG', 2, 4)), list('CD'))
1119+
self.assertEqual(list(self.islice('ABCDEFG', 2, None)), list('CDEFG'))
1120+
self.assertEqual(list(self.islice('ABCDEFG', 0, None, 2)), list('ACEG'))
1121+
# Test items consumed.
1122+
it = iter(xrange(10))
1123+
self.assertEqual(list(self.islice(it, 3)), range(3))
1124+
self.assertEqual(list(it), range(3, 10))
1125+
it = iter(xrange(10))
1126+
self.assertEqual(list(self.islice(it, 3, 3)), [])
1127+
self.assertEqual(list(it), range(3, 10))
1128+
# Test that slice finishes in predictable state.
1129+
c = count()
1130+
self.assertEqual(list(self.islice(c, 1, 3, 50)), [1])
1131+
self.assertEqual(next(c), 3)
1132+
1133+
10871134
class TestGC(unittest.TestCase):
10881135

10891136
def makecycle(self, iterator, container):
@@ -1577,6 +1624,17 @@ def __init__(self, newarg=None, *args):
15771624
... "Return function(0), function(1), ..."
15781625
... return imap(function, count(start))
15791626
1627+
>>> import collections
1628+
>>> def consume(iterator, n=None):
1629+
... "Advance the iterator n-steps ahead. If n is None, consume entirely."
1630+
... # Use functions that consume iterators at C speed.
1631+
... if n is None:
1632+
... # feed the entire iterator into a zero-length deque
1633+
... collections.deque(iterator, maxlen=0)
1634+
... else:
1635+
... # advance to the empty slice starting at position n
1636+
... next(islice(iterator, n, n), None)
1637+
15801638
>>> def nth(iterable, n, default=None):
15811639
... "Returns the nth item or a default value"
15821640
... return next(islice(iterable, n, None), default)
@@ -1678,6 +1736,14 @@ def __init__(self, newarg=None, *args):
16781736
>>> list(islice(tabulate(lambda x: 2*x), 4))
16791737
[0, 2, 4, 6]
16801738
1739+
>>> it = iter(xrange(10))
1740+
>>> consume(it, 3)
1741+
>>> next(it)
1742+
3
1743+
>>> consume(it)
1744+
>>> next(it, 'Done')
1745+
'Done'
1746+
16811747
>>> nth('abcde', 3)
16821748
'd'
16831749
@@ -1753,7 +1819,8 @@ def __init__(self, newarg=None, *args):
17531819
def test_main(verbose=None):
17541820
test_classes = (TestBasicOps, TestVariousIteratorArgs, TestGC,
17551821
RegressionTests, LengthTransparency,
1756-
SubclassWithKwargsTest, TestExamples)
1822+
SubclassWithKwargsTest, TestExamples,
1823+
TestPurePythonRoughEquivalents)
17571824
test_support.run_unittest(*test_classes)
17581825

17591826
# verify reference counting
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Modify documentation for the :func:`islice` recipe to consume initial values
2+
up to the start index.

0 commit comments

Comments
 (0)
0