@@ -1288,12 +1288,16 @@ def product1(*args, **kwds):
1288
1288
else :
1289
1289
return
1290
1290
1291
- def product2 (* args , ** kwds ):
1291
+ def product2 (* iterables , repeat = 1 ):
1292
1292
'Pure python version used in docs'
1293
- pools = list (map (tuple , args )) * kwds .get ('repeat' , 1 )
1293
+ if repeat < 0 :
1294
+ raise ValueError ('repeat argument cannot be negative' )
1295
+ pools = [tuple (pool ) for pool in iterables ] * repeat
1296
+
1294
1297
result = [[]]
1295
1298
for pool in pools :
1296
1299
result = [x + [y ] for x in result for y in pool ]
1300
+
1297
1301
for prod in result :
1298
1302
yield tuple (prod )
1299
1303
@@ -2062,6 +2066,161 @@ def test_islice_recipe(self):
2062
2066
self .assertEqual (next (c ), 3 )
2063
2067
2064
2068
2069
+ def test_tee_recipe (self ):
2070
+
2071
+ # Begin tee() recipe ###########################################
2072
+
2073
+ def tee (iterable , n = 2 ):
2074
+ if n < 0 :
2075
+ raise ValueError ('n must be >= 0' )
2076
+ iterator = iter (iterable )
2077
+ shared_link = [None , None ]
2078
+ return tuple (_tee (iterator , shared_link ) for _ in range (n ))
2079
+
2080
+ def _tee (iterator , link ):
2081
+ try :
2082
+ while True :
2083
+ if link [1 ] is None :
2084
+ link [0 ] = next (iterator )
2085
+ link [1 ] = [None , None ]
2086
+ value , link = link
2087
+ yield value
2088
+ except StopIteration :
2089
+ return
2090
+
2091
+ # End tee() recipe #############################################
2092
+
2093
+ n = 200
2094
+
2095
+ a , b = tee ([]) # test empty iterator
2096
+ self .assertEqual (list (a ), [])
2097
+ self .assertEqual (list (b ), [])
2098
+
2099
+ a , b = tee (irange (n )) # test 100% interleaved
2100
+ self .assertEqual (lzip (a ,b ), lzip (range (n ), range (n )))
2101
+
2102
+ a , b = tee (irange (n )) # test 0% interleaved
2103
+ self .assertEqual (list (a ), list (range (n )))
2104
+ self .assertEqual (list (b ), list (range (n )))
2105
+
2106
+ a , b = tee (irange (n )) # test dealloc of leading iterator
2107
+ for i in range (100 ):
2108
+ self .assertEqual (next (a ), i )
2109
+ del a
2110
+ self .assertEqual (list (b ), list (range (n )))
2111
+
2112
+ a , b = tee (irange (n )) # test dealloc of trailing iterator
2113
+ for i in range (100 ):
2114
+ self .assertEqual (next (a ), i )
2115
+ del b
2116
+ self .assertEqual (list (a ), list (range (100 , n )))
2117
+
2118
+ for j in range (5 ): # test randomly interleaved
2119
+ order = [0 ]* n + [1 ]* n
2120
+ random .shuffle (order )
2121
+ lists = ([], [])
2122
+ its = tee (irange (n ))
2123
+ for i in order :
2124
+ value = next (its [i ])
2125
+ lists [i ].append (value )
2126
+ self .assertEqual (lists [0 ], list (range (n )))
2127
+ self .assertEqual (lists [1 ], list (range (n )))
2128
+
2129
+ # test argument format checking
2130
+ self .assertRaises (TypeError , tee )
2131
+ self .assertRaises (TypeError , tee , 3 )
2132
+ self .assertRaises (TypeError , tee , [1 ,2 ], 'x' )
2133
+ self .assertRaises (TypeError , tee , [1 ,2 ], 3 , 'x' )
2134
+
2135
+ # Tests not applicable to the tee() recipe
2136
+ if False :
2137
+ # tee object should be instantiable
2138
+ a , b = tee ('abc' )
2139
+ c = type (a )('def' )
2140
+ self .assertEqual (list (c ), list ('def' ))
2141
+
2142
+ # test long-lagged and multi-way split
2143
+ a , b , c = tee (range (2000 ), 3 )
2144
+ for i in range (100 ):
2145
+ self .assertEqual (next (a ), i )
2146
+ self .assertEqual (list (b ), list (range (2000 )))
2147
+ self .assertEqual ([next (c ), next (c )], list (range (2 )))
2148
+ self .assertEqual (list (a ), list (range (100 ,2000 )))
2149
+ self .assertEqual (list (c ), list (range (2 ,2000 )))
2150
+
2151
+ # test invalid values of n
2152
+ self .assertRaises (TypeError , tee , 'abc' , 'invalid' )
2153
+ self .assertRaises (ValueError , tee , [], - 1 )
2154
+
2155
+ for n in range (5 ):
2156
+ result = tee ('abc' , n )
2157
+ self .assertEqual (type (result ), tuple )
2158
+ self .assertEqual (len (result ), n )
2159
+ self .assertEqual ([list (x ) for x in result ], [list ('abc' )]* n )
2160
+
2161
+
2162
+ # Tests not applicable to the tee() recipe
2163
+ if False :
2164
+ # tee pass-through to copyable iterator
2165
+ a , b = tee ('abc' )
2166
+ c , d = tee (a )
2167
+ self .assertTrue (a is c )
2168
+
2169
+ # test tee_new
2170
+ t1 , t2 = tee ('abc' )
2171
+ tnew = type (t1 )
2172
+ self .assertRaises (TypeError , tnew )
2173
+ self .assertRaises (TypeError , tnew , 10 )
2174
+ t3 = tnew (t1 )
2175
+ self .assertTrue (list (t1 ) == list (t2 ) == list (t3 ) == list ('abc' ))
2176
+
2177
+ # test that tee objects are weak referencable
2178
+ a , b = tee (range (10 ))
2179
+ p = weakref .proxy (a )
2180
+ self .assertEqual (getattr (p , '__class__' ), type (b ))
2181
+ del a
2182
+ gc .collect () # For PyPy or other GCs.
2183
+ self .assertRaises (ReferenceError , getattr , p , '__class__' )
2184
+
2185
+ ans = list ('abc' )
2186
+ long_ans = list (range (10000 ))
2187
+
2188
+ # Tests not applicable to the tee() recipe
2189
+ if False :
2190
+ # check copy
2191
+ a , b = tee ('abc' )
2192
+ self .assertEqual (list (copy .copy (a )), ans )
2193
+ self .assertEqual (list (copy .copy (b )), ans )
2194
+ a , b = tee (list (range (10000 )))
2195
+ self .assertEqual (list (copy .copy (a )), long_ans )
2196
+ self .assertEqual (list (copy .copy (b )), long_ans )
2197
+
2198
+ # check partially consumed copy
2199
+ a , b = tee ('abc' )
2200
+ take (2 , a )
2201
+ take (1 , b )
2202
+ self .assertEqual (list (copy .copy (a )), ans [2 :])
2203
+ self .assertEqual (list (copy .copy (b )), ans [1 :])
2204
+ self .assertEqual (list (a ), ans [2 :])
2205
+ self .assertEqual (list (b ), ans [1 :])
2206
+ a , b = tee (range (10000 ))
2207
+ take (100 , a )
2208
+ take (60 , b )
2209
+ self .assertEqual (list (copy .copy (a )), long_ans [100 :])
2210
+ self .assertEqual (list (copy .copy (b )), long_ans [60 :])
2211
+ self .assertEqual (list (a ), long_ans [100 :])
2212
+ self .assertEqual (list (b ), long_ans [60 :])
2213
+
2214
+ # Issue 13454: Crash when deleting backward iterator from tee()
2215
+ forward , backward = tee (repeat (None , 2000 )) # 20000000
2216
+ try :
2217
+ any (forward ) # exhaust the iterator
2218
+ del backward
2219
+ except :
2220
+ del forward , backward
2221
+ raise
2222
+
2223
+
2065
2224
class TestGC (unittest .TestCase ):
2066
2225
2067
2226
def makecycle (self , iterator , container ):
0 commit comments