Paul Rudin said:
Isn't it odd that the generator isn't faster, since the comprehension
presumably builds a list first and then iterates over it, whereas the
generator doesn't need to make a list?
The generator doesn't, but the implementation of join then does
(almost). See Objects/stringobject.c line 1745:
seq = PySequence_Fast(orig, "");
As per <
http://docs.python.org/api/sequence.html>,
"""
PyObject* PySequence_Fast(PyObject *o, const char *m)
Return value: New reference.
Returns the sequence o as a tuple, unless it is already a tuple or list,
in which case o is returned. Use PySequence_Fast_GET_ITEM() to access
the members of the result. Returns NULL on failure. If the object is not
a sequence, raises TypeError with m as the message text.
"""
If orig is neither a list nor a tuple, but for example a generator,
PySequence_Fast builds a list from it (even though its docs which I just
quoted says it builds a tuple -- building the list is clearly the right
choice, so I'd say it's the docs that are wrong, not the code;-)... so
in this particular case the usual advantage of the generator disappears.
PySequence_fast is called in 13 separate spots in 8 C files in the
Python 2.5 sources, so there may a few more surprises like this;-).
Alex