string.join(list) vs list.join(string) | Why Python’s Creators Chose The Former

Rate this post

If you’re like me, you may have asked yourself the following question:

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

string.join(list) vs list.join(string) | Why Python’s Creators Chose The Former

The join method works with any iterable, not just with lists. Therefore, you’d have to implement it in hundreds of classes instead of just one (in the string class). Additionally, join() works only on a string delimiter and with an iterable of strings. Thus, it’s specific to strings and shouldn’t be allowed to be applied on, say, a list of integers.

Let’s divide the world into REALITY and FANTASY.

REALITY: You probably know the string.join(list) method in Python to concatenate all strings in the list using string as a delimiter:

lst = ['a', 'b', 'c']
# a-b-c

FANTASY: Many coders find this confusing, wouldn’t it be more intuitive to use list.join(string) rather than string.join(list)?

lst = ['a', 'b', 'c']
# a-b-c

This option of joining a list to a string would feel more object-oriented: you want to join the list and not the string, after all!

So, what are the thought processes behind Python’s creators? Why did they decide to create a join() method on the string rather than the list type?

In this article, I’ve compiled the pro and con arguments from various sources. You can find a detailed list of the sources below.

Argument 1: One Method to Join ‘Em All

You can join any iterable such as a list, tuple, dictionary, or set. But the delimiter must be a string. Thus, you have two options:

  • Require that each iterable implements its own join method (such as __str__).
  • Implement only one join method that works on each iterable.

The latter is far less work for the language creators, and less confusing for the programmer.

['a', 'b', 'c'].join('-')
('a', 'b', 'c').join('-')
{'a', 'b', 'c'}.join('-')
# a-b-c

In the fantasy world, you have many implementations of basically the same method.

['a', 'b', 'c'].join('-')
('a', 'b', 'c').join('-')
{'a', 'b', 'c'}.join('-')
# a-b-c

This would be confusing as every single iterable must implement the same method. Either there would be a lot of redundant code, or the method must be implemented on the iterable object which leads us to the next argument:

Argument 2: Explicit is Better Than Implicit

If you type into your Python shell the two words import this, it’ll print the “Zen of Python”:

The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.

Go ahead and try it yourself, I’ll wait:

The join() method can only join iterables if they contain strings:

lst = [1, 'b', 'c']
# TypeError: sequence item 0: expected str instance, int found

Python will throw a TypeError, if you try to join an iterable that contains at least one non-string element.

Say, we live in FANTASY WORLD and the iterable type implements a join method that’s valid (among others) for all container types such as lists. This would be the only alternative that makes sense and doesn’t add lots of redundant code to the Python library.

This would mean that all iterables containing arbitrary elements could be joined to a string! Even if the elements in the iterable are not of type string. Even if they are absurd data types you created just to confuse Python.

You may ask: so what? But the problem is that Python would have to use an implicit string conversion scheme in case the objects are not of type string. The default string representation is often unreadable or not suitable to appear in a concatenation. Surely, this would add lots and lots of confusion.

For example: what does it, semantically, mean to join a list of integers? Adding them? Concatenating the string representations? Averaging them? There’s no clear answer and any implicit choice would lead to confusion. Explicit is better than implicit!

Note: There’s another problem with implementing a join method on the iterable level! The iterable is not a type but an interface for any type that defines an __iter__ method. Thus, it comes back to requiring all iterables to implement their custom join which is complicated and messy.

Argument 3: Unexpected Dependency of Iterables From String and Unicode

Using list.join() was actually proposed by Skip Montanaro, one of Python’s creators:

On Fri, 11 Jun 1999, Skip Montanaro wrote:

It occurred to me just a few minutes after sending my previous message that
it might make sense to make string.join a method for lists and tuples.
They'd obviously have to make the same type checks that string.join does.
as in:
['spam!', 'eggs!'].join()
'spam! eggs!'

But David replied that he didn’t like the implications from the generalization of join(): the reduce() function:

I like the notion, but I think it would naturally migrate towards
genericity, at which point it might be called "reduce", so that:

['spam!', 'eggs!'].reduce()

['spam!', 'eggs!'].reduce(' ')
'spam! eggs!'

6 # 1 + 2 + 3

26 # 1 + 10 + 2 + 10 + 3

note that string.join(foo) == foo.reduce(' ')
and string.join(foo, '') == foo.reduce()

You can see that this leads to unclear semantics if you have a list of integers. Do we reduce a list of integers by summing them up or concatenating them? What if you pass the value 10 as a separator?

Guido, Python’s benevolent dictator for life, replied (highlights by me):

>>> On Fri, 11 Jun 1999, Skip Montanaro wrote:
>>> It occurred to me just a few minutes after sending my previous message that
>>> it might make sense to make string.join a method for lists and tuples.
>>> They'd obviously have to make the same type checks that string.join does.
>>> as in:
>>> ['spam!', 'eggs!'].join()
>>> 'spam! eggs!'

Note that this is not as powerful as string.join(); the latter works
on any sequence, not just on lists and tuples. (Though that may not
be a big deal.)

I also find it slightly objectionable that this is a general list
method but only works if the list contains only strings; Dave Ascher's
generalization to reduce() is cute but strikes me are more general
than useful, and the name will forever present a mystery to most

Perhaps join() ought to be a built-in function?
--Guido van Rossum (home page:

To summarize this conversation, Guido had two counter arguments:

  • The join() method works on all iterables and not only on lists and tuples.
  • The method must be a general list method but works only if the list contains strings.

The final note is interesting though: join() could have been built-in function, too!

Where to Go From Here?

Enough theory. Let’s get some practice!

Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.

To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

You build high-value coding skills by working on practical coding projects!

Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?

🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!


I combined web resources, email communications, and my own thoughts to write this comprehensive guide. I’m confident that the tutorial covers the real sources—but we’ll never know for 100%, don’t we?