The answer to the question asked in your title, "Which is more pythonic; zip or enumerate...?" is: they both are. enumerate
is just a special case of zip
.
The answer to your more specific question about that for
loop is: use zip
, but not for the reasons you've seen so far.
The biggest advantage of zip
in that loop has nothing to do with zip
itself. It has to do with avoiding the assumptions made in your enumerate
loop. To explain, I'll make two different generators based on your two examples:
def process_items_and_tags(items, tags):
"Do something with two iterables: items and tags."
for item, tag in zip(items, tag):
yield process(item, tag)
def process_items_and_list_of_tags(items, tags_list):
"Do something with an iterable of items and an indexable collection of tags."
for idx, item in enumerate(items):
yield process(item, tags_list[idx])
Both generators can take any iterable as their first argument (items
), but they differ in how they handle their second argument. The enumerate
-based approach can only process tags in a list
-like collection with []
indexing. That rules out a huge number of iterables, like file streams and generators, for no good reason.
Why is one parameter more tightly constrained than the other? The restriction isn't inherent in the problem the user is trying to solve, since the generator could just as easily have been written the other way 'round:
def process_list_of_items_and_tags(items_list, tags):
"Do something with an indexable collection of items and an iterable of tags."
for idx, tag in enumerate(tags):
yield process(items[idx], tag)
Same result, different restriction on the inputs. Why should your caller have to know or care about any of that?
As an added penalty, anything of the form some_list[some_index]
could raise an IndexError
, which you would have to either catch or prevent in some way. That's not normally a problem when your loop both enumerates and accesses the same list-like collection, but here you're enumerating one and then accessing items from another. You'd have to add more code to handle an error that could not have happened in the zip
-based version.
Avoiding the unnecessary idx
variable is also nice, but hardly the deciding difference between the two approaches.
For more on the subject of iterables, generators, and functions that use them, see Ned Batchelder's PyCon US 2013 talk, "Loop Like a Native" (text, 30-minute video).
zip
is designed exactly for tasks like this. Your task is to iterate over each pair, not to iterate over numbers. And Python syntax allows you exactly that. – Domiciliatezip
is more pythonic. – Canfieldmap(lambda x, y:sys.stdout.write(x+" "+y+"\n"),group,tag)
provided the lists are of same length. – Ferry