less awkward is:

some_iter = map(lambda x: x if print(x) else x, some_iter)

The tuple has a ~50% overhead, the case statement ~15%, compared to the generator.

I think that the less awkward syntax solves the problem fine (if you can come up with it). I like that it's explicit rather than requiring someone to know what itertools.tap does.

On Sun, Oct 25, 2020 at 4:27 PM Samuel Freilich via Python-ideas <python-ideas@python.org> wrote:
This seems like it might be a reasonable thing to have in intertools? It's not hard to write this sort of thing with map:

some_iter = map(lambda x: (print(x), x)[1], some_iter)

But it's a little awkward. (Though maybe I'm missing a less awkward way to do that.)

Java has Stream.peek for similar functionality, while Stream.forEach is analogous to Python's map.

On Sun, Oct 25, 2020 at 9:02 AM <anderssonpublic@gmail.com> wrote:
Python has many tools for iteration, such as map and filter, that change an iterator into another iterator without consuming the iterator. These make it simple to deal with many items in a list without consuming excessive memory.

Occasionally it is useful to be able to tap into iterator items and execute a function (such as a side effect to validate elements or print them) without making any changes to the overall iterator. This is similar to the idea of a tap in rxjs: https://rxjs-dev.firebaseapp.com/api/operators/tap.

The proposed interface would be:
def generate_items() -> list[int]:
    some_iter = range(10)
    some_iter = tap(assert_int, some_iter)
    some_iter = tap(print, some_iter)
    return list(some_iter)

This would be useful to (for example):
1. Debug chained iterators without a debugger (at the moment you would have to convert the list and then print the whole list or include a print statement in one of the chained functions)
2. Check that items in an iterator conform to assumptions and raising exceptions if they do not
3. Improve type hints in editors since after the assert is executed all items conform to the assert in the tap (for example, they are an integer)

The implementation would be quite simple (at least in python):
def tap(func: typing.Callable[[T], typing.Any], iter: typing.Iterable[T]) -> typing.iterable[T]:
    for item in iter:
        func(item)
        yield item

Thoughts?
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/7Q3NR4SKUC72PVQ3APK2HL2HNX3HP2IE/
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/7AUUGTM77ZJFJ4PSZUYHB2PW2VJKBYYT/
Code of Conduct: http://python.org/psf/codeofconduct/