Categories
list python string

Why is it string.join(list) instead of list.join(string)?

2004

This has always confused me. It seems like this would be nicer:

["Hello", "world"].join("-")

Than this:

"-".join(["Hello", "world"])

Is there a specific reason it is like this?

7

  • 4

    For easy memory and understanding, - declares that you are joining a list and converting to a string.It’s result oriented.

    Dec 4, 2017 at 12:26

  • 7

    I think the original idea is that because join() returns a string, it would have to be called from the string context. Putting join() on a list doesn’t make a ton of sense in that a list is a container of objects and shouldn’t have a one-off function specific to only strings.

    May 29, 2019 at 14:46

  • 4

    @BallpointBen “…because Python’s type system isn’t strong enough” is exactly wrong. As Yoshiki Shibukawa’s answer (from 8 years before you comment!) says, iterable.join() was considered as possibility but was rejected because it’s a less good API design – not because it wasn’t possible to implement.

    Jun 12, 2020 at 9:39

  • 6

    I may be biased because I am used to javascript, but you want to join the list, it should be a method of list imo. It feels backwards.

    Jan 9, 2021 at 5:00

  • 3

    Well, str.split() returns a non-string and makes quite a bit of sense. It seems like the same logic should be ok here, right? (Just talking about the conceptual problem of a non-string output)

    – ntjess

    Jan 14 at 19:19


1387

It’s because any iterable can be joined (e.g, list, tuple, dict, set), but its contents and the “joiner” must be strings.

For example:

'_'.join(['welcome', 'to', 'stack', 'overflow'])
'_'.join(('welcome', 'to', 'stack', 'overflow'))
'welcome_to_stack_overflow'

Using something other than strings will raise the following error:

TypeError: sequence item 0: expected str instance, int found

11

  • 98

    I do not agree conceptually even if It makes sense codewise. list.join(string) appears more an object-oriented approach whereas string.join(list) sounds much more procedural to me.

    Jan 14, 2018 at 15:35


  • 39

    So why isn’t it implemented on iterable?

    Mar 28, 2018 at 10:50

  • 18

    @TimeSheep: A list of integers doesn’t have a meaningful join, even though it’s iterable.

    – recursive

    Mar 28, 2018 at 17:21

  • 24

    I have tried to use print(str.join('-', my_list)) and it works, feels better.

    – pimgeek

    Jun 1, 2018 at 6:29

  • 29

    @TimeSheep Because iterable is not a concrete type, iterable is an interface, any type that defines an __iter__ method. Requiring all iterables to also implement join would complicate a general interface (which also covers iterables over non-strings) for a very particular use case. Defining join on strins side-steps this problem at the cost of the “unintuitive” order. A better choice might have been to keep it a function with the first argument being the iterable and the second (optional one) being the joiner string – but that ship has sailed.

    Jun 11, 2018 at 6:08


399

This was discussed in the String methods… finally thread in the Python-Dev achive, and was accepted by Guido. This thread began in Jun 1999, and str.join was included in Python 1.6 which was released in Sep 2000 (and supported Unicode). Python 2.0 (supported str methods including join) was released in Oct 2000.

  • There were four options proposed in this thread:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join as a built-in function
  • Guido wanted to support not only lists and tuples, but all sequences/iterables.
  • seq.reduce(str) is difficult for newcomers.
  • seq.join(str) introduces unexpected dependency from sequences to str/unicode.
  • join() as a built-in function would support only specific data types. So using a built-in namespace is not good. If join() supports many datatypes, creating an optimized implementation would be difficult, if implemented using the __add__ method then it would ve O(n²).
  • The separator string (sep) should not be omitted. Explicit is better than implicit.

Here are some additional thoughts (my own, and my friend’s):

  • Unicode support was coming, but it was not final. At that time UTF-8 was the most likely about to replace UCS2/4. To calculate total buffer length of UTF-8 strings it needs to know character coding rule.
  • At that time, Python had already decided on a common sequence interface rule where a user could create a sequence-like (iterable) class. But Python didn’t support extending built-in types until 2.2. At that time it was difficult to provide basic iterable class (which is mentioned in another comment).

Guido’s decision is recorded in a historical mail, deciding on str.join(seq):

Funny, but it does seem right! Barry, go for it…
Guido van Rossum

3

  • 1

    Nice, this documents the reasoning. It’d be nice to know more about the “unexpected dependency from sequences to str/unicode.” — and whether that is still so.

    – zdim

    Mar 16 at 16:59

  • This is the best answer, as it provides the authoritative background and reasons it was chosen.

    – tschwab

    Jul 12 at 15:58

  • @zdim Yes, IMO it’s an odd dependency to be concerned about.

    2 days ago

257

Because the join() method is in the string class, instead of the list class?

I agree it looks funny.

See http://www.faqs.org/docs/diveintopython/odbchelper_join.html:

Historical note. When I first learned
Python, I expected join to be a method
of a list, which would take the
delimiter as an argument. Lots of
people feel the same way, and there’s
a story behind the join method. Prior
to Python 1.6, strings didn’t have all
these useful methods. There was a
separate string module which contained
all the string functions; each
function took a string as its first
argument. The functions were deemed
important enough to put onto the
strings themselves, which made sense
for functions like lower, upper, and
split. But many hard-core Python
programmers objected to the new join
method, arguing that it should be a
method of the list instead, or that it
shouldn’t move at all but simply stay
a part of the old string module (which
still has lots of useful stuff in it).
I use the new join method exclusively,
but you will see code written either
way, and if it really bothers you, you
can use the old string.join function
instead.

— Mark Pilgrim, Dive into Python

2

  • 16

    The Python 3 string library has removed all the redundant str methods, so you no longer can use string.join(). Personally, I never thought it ‘funny’, it makes perfect sense, as you can join much more than just lists, but the joiner is always a string!

    – Martijn Pieters

    Aug 31, 2018 at 11:51

  • One could use str.join(str_instance, list[str]) instead of string.join(str_instance, list[str]). There’s also the advantage you don’t need to import any modules for such a basic functionality.

    yesterday