@@ -860,32 +860,62 @@ def __reduce__(self):
860
860
return (type (self ), (self .data , self .selectors ))
861
861
862
862
863
- def tee (iterable , n = 2 ):
864
- import collections
865
- class _tee :
866
- @__graalpython__ .builtin_method
867
- def __init__ (self , it , deque , deques ):
863
+ class _tee :
864
+ # This uses a linked list of fixed size lists where
865
+ # the last item in the fixed size list is a link to
866
+ # another fixed size list. Once all _tee instances have
867
+ # traversed given fixed size list, it'll become a garbage
868
+ # to be collected
869
+ @__graalpython__ .builtin_method
870
+ def __init__ (self , it , buffer = None ):
871
+ self .itemIndex = 0
872
+ if buffer is None :
873
+ # Support for direct creation of _tee from user code,
874
+ # where the ctor takes a single iterable object
875
+ self .it = iter (it )
876
+ self .buffer = [None ] * 8
877
+ else :
868
878
self .it = it
869
- self .deque = deque
870
- self .deques = deques
879
+ self .buffer = buffer
880
+
881
+ @__graalpython__ .builtin_method
882
+ def __iter__ (self ):
883
+ return self
884
+
885
+ @__graalpython__ .builtin_method
886
+ def __next__ (self ):
887
+ # jump to the next buffer if necessary
888
+ lastIndex = len (self .buffer ) - 1
889
+ if self .itemIndex == lastIndex :
890
+ if self .buffer [lastIndex ] is None :
891
+ self .buffer [lastIndex ] = [None ] * 8
892
+ self .buffer = self .buffer [lastIndex ]
893
+ self .itemIndex = 0
894
+ # take existing item from the buffer or advance the iterator
895
+ if self .buffer [self .itemIndex ] is None :
896
+ result = next (self .it )
897
+ self .buffer [self .itemIndex ] = result
898
+ else :
899
+ result = self .buffer [self .itemIndex ]
900
+ self .itemIndex += 1
901
+ return result
871
902
872
- @__graalpython__ .builtin_method
873
- def __iter__ (self ):
874
- return self
903
+ @__graalpython__ .builtin_method
904
+ def __copy__ (self ):
905
+ return _tee ( self . it , self . buffer )
875
906
876
- @__graalpython__ .builtin_method
877
- def __next__ (self ):
878
- if not self .deque :
879
- newval = next (self .it )
880
- for d in self .deques :
881
- d .append (newval )
882
- return self .deque .popleft ()
883
907
908
+ def tee (iterable , n = 2 ):
884
909
if not isinstance (n , int ):
885
910
raise TypeError ()
886
911
if n < 0 :
887
912
raise ValueError ("n must be >=0" )
888
-
889
- deques = [ collections . deque () for i in range ( n )]
913
+ # if the iterator can be copied, use that instead of _tee
914
+ # note: this works for _tee itself
890
915
it = iter (iterable )
891
- return tuple (_tee (it , d , deques ) for d in deques )
916
+ copy = getattr (it , "__copy__" , None )
917
+ if callable (copy ):
918
+ return tuple ([it ] + [it .__copy__ () for i in range (1 , n )])
919
+ else :
920
+ queue = [None ] * 8
921
+ return tuple (_tee (it , queue ) for i in range (0 , n ))
0 commit comments