Skip to content

Commit c13b55f

Browse files
committed
add an overload to take() that takes a slice
1 parent e1dd5d7 commit c13b55f

File tree

5 files changed

+82
-13
lines changed

5 files changed

+82
-13
lines changed

doc/api/types_linq.enumerable.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,6 +2061,30 @@ Example
20612061

20622062
----
20632063

2064+
instancemethod ``take(__index)``
2065+
----------------------------------
2066+
2067+
Parameters
2068+
- `__index` (``slice``)
2069+
2070+
Returns
2071+
- ``Enumerable[TSource_co]``
2072+
2073+
Produces a subsequence defined by the given slice notation.
2074+
2075+
This method currently is identical to `elements_in()` when it takes a slice.
2076+
2077+
Example
2078+
.. code-block:: python
2079+
2080+
>>> def gen():
2081+
... yield 1; yield 10; yield 100; yield 1000; yield 10000
2082+
2083+
>>> Enumerable(gen()).take(slice(1, 3)).to_list()
2084+
[10, 100]
2085+
2086+
----
2087+
20642088
instancemethod ``take_last(count)``
20652089
-------------------------------------
20662090

@@ -2461,6 +2485,9 @@ Returns
24612485

24622486
Produces a subsequence defined by the given slice notation.
24632487

2488+
This method always uses a generic list slicing method regardless the implementation of the
2489+
wrapped iterable.
2490+
24642491
Example
24652492
.. code-block:: python
24662493
@@ -2485,6 +2512,8 @@ Returns
24852512

24862513
Produces a subsequence with indices that define a slice.
24872514

2515+
This method always uses a generic list slicing method regardless the implementation of the
2516+
wrapped iterable.
24882517

24892518
Example
24902519
.. code-block:: python

doc/to-start/differences.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ and its .NET counterpart.
3636
Some C# methods return such default values if the source sequence is empty, or skip ``null``'s if the source sequence contains
3737
concrete data too. There are no such notions in Python and the C#-like default semantics are non-existent. So, this usage is
3838
not supported by this library (Can ``None`` be considered a default value for all cases? Hmm..).
39+
* C# has `Index <https://docs.microsoft.com/en-us/dotnet/api/system.index>`_ syntaxes, and to be Pythonic, these are
40+
negative indices. C# has `Range <https://docs.microsoft.com/en-us/dotnet/api/system.range>`_, which are
41+
`slices <https://docs.python.org/3/library/functions.html#slice>`_. This difference can be seen in ``Enumerable.element_at()``
42+
and ``Enumerable.take()``.
3943
* All classes in this library are concrete. There are no interfaces like what are usually done in C#.
4044

4145
Limitations:

tests/test_usage.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,28 +1349,38 @@ def test_sum2_overload2(self):
13491349

13501350

13511351
class TestTakeMethod:
1352-
def test_some(self):
1352+
def test_overload1_some(self):
13531353
lst = [1, 4, 6, 9, 10]
13541354
en = Enumerable(lst)
13551355
assert en.take(3).to_list() == [1, 4, 6]
13561356
assert en.take(5).to_list() == lst
13571357

1358-
def test_more(self):
1358+
def test_overload1_more(self):
13591359
lst = [1, 4, 6, 9, 10]
13601360
en = Enumerable(lst)
13611361
assert en.take(6).to_list() == lst
13621362

1363-
def test_count_zero_or_negative(self):
1363+
def test_overload1_count_zero_or_negative(self):
13641364
lst = [1, 4, 6, 9, 10]
13651365
en = Enumerable(lst)
13661366
assert en.take(0).to_list() == []
13671367
assert en.take(-1).to_list() == []
13681368

1369-
def test_id(self):
1369+
def test_overload1_id(self):
13701370
lst = [1, 4, 6, 9, 10]
13711371
en = Enumerable(lst)
13721372
assert en.take(4).concat(en.skip(4)).to_list() == lst
13731373

1374+
def test_overload2(self):
1375+
def gen():
1376+
yield from [1, 10, 100, 1000, 10000]
1377+
en = Enumerable(gen())
1378+
assert en.take(slice(1, 3)).to_list() == [10, 100]
1379+
1380+
def test_overload2_fallback(self):
1381+
en = Enumerable(TestElementAtMethod.OnlyHasGetItem(['x']))
1382+
assert en.elements_in(slice(7)).to_list() == ['x']
1383+
13741384

13751385
class TestTakeLastMethod:
13761386
def test_some(self):

types_linq/enumerable.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -841,15 +841,18 @@ def sum2(self, *args) -> Any:
841841
selector, default = args
842842
return self._sum_helper(selector, lambda: default)
843843

844-
def take(self, count: int) -> Enumerable[TSource_co]:
845-
def inner():
846-
iterator = iter(self)
847-
try:
848-
for _ in range(count):
849-
yield next(iterator)
850-
except StopIteration:
851-
return
852-
return Enumerable(inner)
844+
def take(self, count: Union[int, slice]) -> Enumerable[TSource_co]:
845+
if isinstance(count, int):
846+
def inner():
847+
iterator = iter(self)
848+
try:
849+
for _ in range(count):
850+
yield next(iterator)
851+
except StopIteration:
852+
return
853+
return Enumerable(inner)
854+
else: # isinstance(count, slice)
855+
return self.elements_in(count)
853856

854857
def take_last(self, count: int) -> Enumerable[TSource_co]:
855858
if count <= 0:

types_linq/enumerable.pyi

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,7 @@ class Enumerable(Sequence[TSource_co], Generic[TSource_co]):
14901490
880
14911491
'''
14921492

1493+
@overload
14931494
def take(self, count: int) -> Enumerable[TSource_co]:
14941495
'''
14951496
Returns a specified number of contiguous elements from the start of the sequence.
@@ -1500,6 +1501,23 @@ class Enumerable(Sequence[TSource_co], Generic[TSource_co]):
15001501
[98, 92, 85]
15011502
'''
15021503

1504+
@overload
1505+
def take(self, __index: slice) -> Enumerable[TSource_co]:
1506+
'''
1507+
Produces a subsequence defined by the given slice notation.
1508+
1509+
This method currently is identical to `elements_in()` when it takes a slice.
1510+
1511+
Example
1512+
.. code-block:: python
1513+
1514+
>>> def gen():
1515+
... yield 1; yield 10; yield 100; yield 1000; yield 10000
1516+
1517+
>>> Enumerable(gen()).take(slice(1, 3)).to_list()
1518+
[10, 100]
1519+
'''
1520+
15031521
def take_last(self, count: int) -> Enumerable[TSource_co]:
15041522
'''
15051523
Returns a new sequence that contains the last `count` elements.
@@ -1737,6 +1755,9 @@ class Enumerable(Sequence[TSource_co], Generic[TSource_co]):
17371755
'''
17381756
Produces a subsequence defined by the given slice notation.
17391757
1758+
This method always uses a generic list slicing method regardless the implementation of the
1759+
wrapped iterable.
1760+
17401761
Example
17411762
.. code-block:: python
17421763
@@ -1752,6 +1773,8 @@ class Enumerable(Sequence[TSource_co], Generic[TSource_co]):
17521773
'''
17531774
Produces a subsequence with indices that define a slice.
17541775
1776+
This method always uses a generic list slicing method regardless the implementation of the
1777+
wrapped iterable.
17551778
17561779
Example
17571780
.. code-block:: python

0 commit comments

Comments
 (0)