Skip to content

Commit e397bfd

Browse files
committed
Update tee() rough equivalent recipe and related tests
1 parent 2a7a4d3 commit e397bfd

File tree

2 files changed

+76
-50
lines changed

2 files changed

+76
-50
lines changed

Doc/library/itertools.rst

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -689,23 +689,38 @@ loops that truncate the stream.
689689

690690
Roughly equivalent to::
691691

692+
class _tee:
693+
694+
def __init__(self, iterable):
695+
it = iter(iterable)
696+
if isinstance(it, _tee):
697+
self.iterator = it.iterator
698+
self.link = it.link
699+
else:
700+
self.iterator = it
701+
self.link = [None, None]
702+
703+
def __iter__(self):
704+
return self
705+
706+
def __next__(self):
707+
link = self.link
708+
if link[1] is None:
709+
link[0] = next(self.iterator)
710+
link[1] = [None, None]
711+
value, self.link = link
712+
return value
713+
692714
def tee(iterable, n=2):
693715
if n < 0:
694-
raise ValueError('n must be >= 0')
695-
iterator = iter(iterable)
696-
shared_link = [None, None]
697-
return tuple(_tee(iterator, shared_link) for _ in range(n))
698-
699-
def _tee(iterator, link):
700-
try:
701-
while True:
702-
if link[1] is None:
703-
link[0] = next(iterator)
704-
link[1] = [None, None]
705-
value, link = link
706-
yield value
707-
except StopIteration:
708-
return
716+
raise ValueError
717+
if n == 0:
718+
return ()
719+
first = _tee(iterable)
720+
result = [first]
721+
for _ in range(n - 1):
722+
result.append(_tee(first))
723+
return tuple(result)
709724

710725
When the input *iterable* is already a tee iterator object, all
711726
members of the return tuple are constructed as if they had been

Lib/test/test_itertools.py

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,23 +1758,38 @@ def test_tee_recipe(self):
17581758

17591759
# Begin tee() recipe ###########################################
17601760

1761+
class _tee:
1762+
1763+
def __init__(self, iterable):
1764+
it = iter(iterable)
1765+
if isinstance(it, _tee):
1766+
self.iterator = it.iterator
1767+
self.link = it.link
1768+
else:
1769+
self.iterator = it
1770+
self.link = [None, None]
1771+
1772+
def __iter__(self):
1773+
return self
1774+
1775+
def __next__(self):
1776+
link = self.link
1777+
if link[1] is None:
1778+
link[0] = next(self.iterator)
1779+
link[1] = [None, None]
1780+
value, self.link = link
1781+
return value
1782+
17611783
def tee(iterable, n=2):
17621784
if n < 0:
1763-
raise ValueError('n must be >= 0')
1764-
iterator = iter(iterable)
1765-
shared_link = [None, None]
1766-
return tuple(_tee(iterator, shared_link) for _ in range(n))
1767-
1768-
def _tee(iterator, link):
1769-
try:
1770-
while True:
1771-
if link[1] is None:
1772-
link[0] = next(iterator)
1773-
link[1] = [None, None]
1774-
value, link = link
1775-
yield value
1776-
except StopIteration:
1777-
return
1785+
raise ValueError
1786+
if n == 0:
1787+
return ()
1788+
first = _tee(iterable)
1789+
result = [first]
1790+
for _ in range(n - 1):
1791+
result.append(_tee(first))
1792+
return tuple(result)
17781793

17791794
# End tee() recipe #############################################
17801795

@@ -1820,12 +1835,10 @@ def _tee(iterator, link):
18201835
self.assertRaises(TypeError, tee, [1,2], 'x')
18211836
self.assertRaises(TypeError, tee, [1,2], 3, 'x')
18221837

1823-
# Tests not applicable to the tee() recipe
1824-
if False:
1825-
# tee object should be instantiable
1826-
a, b = tee('abc')
1827-
c = type(a)('def')
1828-
self.assertEqual(list(c), list('def'))
1838+
# tee object should be instantiable
1839+
a, b = tee('abc')
1840+
c = type(a)('def')
1841+
self.assertEqual(list(c), list('def'))
18291842

18301843
# test long-lagged and multi-way split
18311844
a, b, c = tee(range(2000), 3)
@@ -1846,21 +1859,19 @@ def _tee(iterator, link):
18461859
self.assertEqual(len(result), n)
18471860
self.assertEqual([list(x) for x in result], [list('abc')]*n)
18481861

1862+
# tee objects are independent (see bug gh-123884)
1863+
a, b = tee('abc')
1864+
c, d = tee(a)
1865+
e, f = tee(c)
1866+
self.assertTrue(len({a, b, c, d, e, f}) == 6)
18491867

1850-
# Tests not applicable to the tee() recipe
1851-
if False:
1852-
# tee pass-through to copyable iterator
1853-
a, b = tee('abc')
1854-
c, d = tee(a)
1855-
self.assertTrue(a is c)
1856-
1857-
# test tee_new
1858-
t1, t2 = tee('abc')
1859-
tnew = type(t1)
1860-
self.assertRaises(TypeError, tnew)
1861-
self.assertRaises(TypeError, tnew, 10)
1862-
t3 = tnew(t1)
1863-
self.assertTrue(list(t1) == list(t2) == list(t3) == list('abc'))
1868+
# test tee_new
1869+
t1, t2 = tee('abc')
1870+
tnew = type(t1)
1871+
self.assertRaises(TypeError, tnew)
1872+
self.assertRaises(TypeError, tnew, 10)
1873+
t3 = tnew(t1)
1874+
self.assertTrue(list(t1) == list(t2) == list(t3) == list('abc'))
18641875

18651876
# test that tee objects are weak referencable
18661877
a, b = tee(range(10))

0 commit comments

Comments
 (0)