Joining a list:
>>> \'\'.join([ str(_) for _ in xrange(10) ])
\'0123456789\'
join
must take an iterable.
Appa
If it's in parens, but not brackets, it's technically a generator expression. Generator expressions were first introduced in Python 2.4.
http://wiki.python.org/moin/Generators
The part after the join, ( str(_) for _ in xrange(10) )
is, by itself, a generator expression. You could do something like:
mylist = (str(_) for _ in xrange(10))
''.join(mylist)
and it means exactly the same thing that you wrote in the second case above.
Generators have some very interesting properties, not the least of which is that they don't end up allocating an entire list when you don't need one. Instead, a function like join "pumps" the items out of the generator expression one at a time, doing its work on the tiny intermediate parts.
In your particular examples, list and generator probably don't perform terribly differently, but in general, I prefer using generator expressions (and even generator functions) whenever I can, mostly because it's extremely rare for a generator to be slower than a full list materialization.