Coding style

Patrick Maupin pmaupin at gmail.com
Thu Jul 20 00:25:38 EDT 2006


Terry Reedy wrote:
>
> > Carl Banks wrote:
> >>     def process_values(lst):
> >>         if not lst:
> >>             return
> >>         do_expensive_initialization_step()
> >>         for item in lst:
> >>             do_something_with(item)
> >>         do_expensive_finalization_step()
>
> Given that the input is going to be scanned by an iterator, it really makes
> sense to me to accept an iterator as input.  (Unless the function is for
> special-case usage in a predefined, never to be expanded, set of
> circumstances in which such generality is not needed.  In such a case, if
> lst: is no problem.)  I think the following, untested rewrite suffices.
>
> def process_values(lst):
>     it = iter(lst)
>     try:
>         item = it.next()
>         do_expensive_initialization_step()
>         do_something_with(item) # see note
>         for item in it:
>             do_something_with(item)
>         do_expensive_finalization_step()
>     except StopIteration:
>         pass # or any special empty input code
>
> # note: if writing do_something_with(item) is bothersome,
> # replace 3 lines with
>         while True:
>             do_something_with(item)
>             try:
>                item = it.next
>             except StopIteration:
>                 break
>
> In general, to work with iterators instead of containers, change
>
> if container:
>     do_stuff()
> else:
>     do_empty()
>
> to
>
> try:
>     item = it.next()
>     do_modified_stuff() # taking into account that have item already
> except StopIteration:
>     do_empty()
>

Good example.    But if you want, you can also do the same thing
without explicitly dealing with StopIteration exceptions, by (ab)using
the for statment to deal with the StopIteration exceptions implicitly
for you:

    def process_values(lst):
        it = iter(lst)
        # This for statement executes 0 or 1 times
        for item in it:
            do_expensive_initialization_step()
            do_something_with(item)
            break
        else:
            # Empty list code, if needed, goes here...
            return
        # This for statement executes max(0, len(lst) - 1) times
        for item in it:
            do_something_with(item)
        do_expensive_finalization_step()

And, of course, if you really want to defer optimizations, and you
expect the code to not be in a critical speed path and to not be passed
huge lists or iterators, you could take the original function and add:

        lst = list(lst)

as the first line.  Frankly, if I had a working function in a piece of
code, and then a failure because it was being called with a generator,
I would find this very tempting unless I strongly suspected it would
cause issues later.

Regards,
Pat




More information about the Python-list mailing list