330

In Python, I can do:

>>> list = ['a', 'b', 'c']
>>> ', '.join(list)
'a, b, c'

Is there any easy way to do the same when I have a list of objects?

>>> class Obj:
...     def __str__(self):
...         return 'name'
...
>>> list = [Obj(), Obj(), Obj()]
>>> ', '.join(list)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected string, instance found

Or do I have to resort to a for loop?

Mat
  • 82,161
  • 34
  • 89
  • 109

4 Answers4

487

You could use a list comprehension or a generator expression instead:

', '.join([str(x) for x in list])  # list comprehension
', '.join(str(x) for x in list)    # generator expression
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 3
    or a generator expression: ', '.join(str(x) for x in list) – dF. Jan 31 '09 at 00:12
  • 1
    any idea on which of them would be faster? – gozzilli Mar 23 '12 at 13:29
  • My experiments says that the list comprehension one can be a good 60% faster on small lists (experiment run 10^6 times on a list of three object()s). However, their performance is similar on big lists (2nd experiment run once on a 10^7 objects() list). – gozzilli Mar 23 '12 at 13:42
  • 3
    for a good 30% speed-up (over generator expression above), one can use the supposedly less readable `map` expression (below). – K3---rnc Aug 03 '13 at 15:49
  • 2
    This answer is objectively worse than the `map` solution. – PascalVKooten Oct 12 '15 at 16:27
  • Should not use str, should instead use .encode('utf-8') – Andrew Mar 03 '16 at 22:29
106

The built-in string constructor will automatically call obj.__str__:

''.join(map(str,list))
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
  • 1
    map() doesn't change the list, it's equivalent to [str(o) for o in list] – dF. Jan 31 '09 at 00:26
  • 12
    +1: Map is a good approach; "changing the list" isn't an accurate comment. – S.Lott Jan 31 '09 at 02:32
  • 3
    (another) +1.. map is not less readable, just need to know what the map function does – lapax Apr 10 '15 at 08:20
  • Be aware that map does make pylint complain (as does a lot of things but..), it's prefered to use list comprehension because map has been superseded by list comprehension and it's more pythonic. – Michael Aug 06 '15 at 00:54
  • 1
    @Michael Not correct. `reduce` was the one that was removed, because it usually left people guessing and thus wasn't "pythonic". `map` on the other hand is not an issue. – PascalVKooten Oct 12 '15 at 16:28
  • @PascalvKooten - I was refering to this: http://stackoverflow.com/questions/3569134/why-doesnt-pylint-like-built-in-functions and map definitely generates pylint warnings for me. – Michael Oct 12 '15 at 18:21
  • @Michael Ah my bad, I somehow missed that you were talking in "pylint's name". – PascalVKooten Oct 12 '15 at 18:53
  • 1
    (another) +1: coming from the Perl world this is the most common thing in the universe: join("sep", list) - and all elements of list get converted to their string representations. I've been really struggling to find a solution in python. – jester66 Feb 23 '18 at 13:12
3

I know this is a super old post, but I think what is missed is overriding __repr__, so that __repr__ = __str__, which is the accepted answer of this question marked duplicate.

NotAnAmbiTurner
  • 2,553
  • 2
  • 21
  • 44
1

another solution is to override the join operator of the str class.

Let us define a new class my_string as follows

class my_string(str):
    def join(self, l):
        l_tmp = [str(x) for x in l]
        return super(my_string, self).join(l_tmp)

Then you can do

class Obj:
    def __str__(self):
        return 'name'

list = [Obj(), Obj(), Obj()]
comma = my_string(',')

print comma.join(list)

and you get

name,name,name

BTW, by using list as variable name you are redefining the list class (keyword) ! Preferably use another identifier name.

Hope you'll find my answer useful.

  • Never do that, defining your own classes for builtin types makes your code really hard to integrate – Grigory Apr 21 '22 at 11:02