Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Implicit string concatenation PREMIUM

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
5 min. read 4 min. video Python 3.9—3.13
Python Morsels
Watch as video
03:30

Let's talk about implicit string concatenation in Python.

Strings next to each other

Take a look at this line of Python code:

>>> print("Hello" "world!")

It looks kind of like we're passing multiple arguments to the built-in print function.

But we're not:

>>> print("Hello" "world!")
Helloworld!

If we pass multiple arguments to print, Python will put spaces between those values when printing:

>>> print("Hello", "world!")
Hello world!

But Python wasn't doing.

Our code from before didn't have commas to separate the arguments (note the missing comma between "Hello" and "world!"):

>>> print("Hello" "world!")
Helloworld!

How is that possible? This seems like it should have resulted in a SyntaxError!

Implicit string concatenation

A string literal is the syntax that we use in Python to create brand new strings:

>>> "This is a string"
'This is a string'
>>> 'This is also a string'
'This is also a string'
>>> """Here's another string"""
"Here's another string"

String literals involve using some variation of quote characters to create a string.

If we write multiple string literals next to each other, Python will concatenate those strings:

>>> message = "Hello" "world"
>>> message
'Helloworld'

It's as if we put a plus sign (+) between them. But we didn't:

>>> message = "Hello" "world"
>>> message
'Helloworld'
>>> message = "Hello" + "world"
>>> message
'Helloworld'

This is called implicit string concatenation.

It's a bit of an odd feature, but it can be handy sometimes.

Implicit line continuations with implicit string concatenation

Take a look at this very long string:

long_string = "This is a very long string that goes on and on and might even wrap to the next line in your editor, which may make it challenging to read."

We could break this string up into smaller strings over multiple lines, and then concatenate the substrings together:

long_string = (
    "This is a very long string that goes on and on " +
    "and might even wrap to the next line in your editor, " +
    "which may make it challenging to read."
)

We're using the plus operator to concatenate these strings.

But since they're all string literals, we could instead rely on implicit string concatenation:

long_string = (
    "This is a very long string that goes on and on "
    "and might even wrap to the next line in your editor, "
    "which may make it challenging to read."
)

This is the situation where you're most likely to see implicit string concatenation used: to break up a long string literal into smaller strings over multiple lines, putting parentheses around them to create an implicit line continuation. And relying on the lack of an operator between those literals to make implicit string concatenation happen.

Even in a situation like this one, I usually prefer to avoid implicit string concatenation, as I find that it often causes more confusion than it's worth.

Also, there's usually a better alternative.

Concatenating lines of text

For example, here's some code that uses implicit string concatenation to concatenate many strings that represent lines of text into a single string:

def copyright():
    print(
        "Copyright (c) 1991-2000 ACME Corp\n"
        "All Rights Reserved.\n\n"
        "Copyright (c) 2000-2030 Cyberdyne\n"
        "All Rights Reserved."
    )

You might think that we could use a multiline string for this instead. And we could.

But we would need to remove the indentation from our string, which looks a bit odd:

def copyright():
    print("""
Copyright (c) 1991-2000 ACME Corp
All Rights Reserved.

Copyright (c) 2000-2030 Cyberdyne
All Rights Reserved.""".strip("\n")
    )

But Python has a dedent function in the textwrap module just for this situation:

from textwrap import dedent

def copyright():
    print(dedent("""
        Copyright (c) 1991-2000 ACME Corp
        All Rights Reserved.

        Copyright (c) 2000-2030 Cyberdyne
        All Rights Reserved.
    """).strip("\n"))

For representing multiple lines of text, I prefer to use multiline strings and the textwrap.dedent function instead of using implicit string concatenation.

Bugs caused by implicit string concatenation

Another reason I tend to avoid implicit string concatenation is that it can sometimes cause bugs by accidentally using it.

Here's some code that makes a list of strings:

task_list = [
    "Buy groceries",
    "Do dishes",
    "Do laundry",
    "Practice Python"
]

This list has four strings in it:

>>> len(task_list)
4

Let's modify this list to move the last string to the beginning:

task_list = [
    "Practice Python"
    "Buy groceries",
    "Do dishes",
    "Do laundry",
]

When we do this, we'll see that this list no longer has the same length:

>>> len(task_list)
3

It now has only three items!

This happened because we're now accidentally using implicit string concatenation:

>>> task_list
['Practice PythonBuy groceries', 'Do dishes', 'Do laundry']

We don't have a comma after the first item in our list, so Python is concatenating that string literal with the one just after it:

task_list = [
    "Practice Python"
    "Buy groceries",
    "Do dishes",
    "Do laundry",
]

This is one of the reasons that I prefer to use trailing commas after the last item in a multiline list, or the last argument in a multiline function call:

task_list = [
    "Buy groceries",
    "Do dishes",
    "Do laundry",
    "Practice Python",
]

This can help avoid an accidental use of implicit string concatenation.

Linter rules for implicit string concatenation

Code linters like Pylint, Flake8, and Ruff can often be configured to disallow the use of implicit string concatenation.

For example, for Flake8 you can install the flake8-no-implicit-concat plugin:

$ pip install flake8-no-implicit-concat

And for Ruff, you can enable implicit string concatenation checking and also disable multi-line implicit string concatenation:

[tool.ruff.lint]
select = ["ISC"]

[tool.ruff.lint.flake8-implicit-str-concat]
allow-multiline = false

So if you find this feature jarring enough that you'd like to never use it in your code, you can probably configure your linter to disallow it entirely.

Implicit string concatenation is a bug and a feature

The next time you see two string literals next to each other, keep in mind that Python will automatically concatenate them.

Some folks rely on this as a feature, but others consider it a bug waiting to happen.

Python Morsels
Watch as video
03:30
This is a free preview of a premium screencast. You have 2 previews remaining.