265

I'm trying to write PEP-8 compliant code for a domestic project and I have a line with an f-string that is more than 80 characters long

– the solid thin line near the dot at self.text is the 80 char mark.

I'm trying to split it into different lines in the most pythonic way but the only aswer that actually works is an error for my linter

Working code:

def __str__(self):
    return f'{self.date} - {self.time},\nTags:' + \
    f' {self.tags},\nText: {self.text}'

Output:

2017-08-30 - 17:58:08.307055,
Tags: test tag,
Text: test text

The linter thinks that I'm not respecting E122 from PEP-8, is there a way to get the string right and the code compliant?

Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Owlzy
  • 3,352
  • 2
  • 12
  • 12
  • You dont have to return it all on one line, create a base string in a variable then append each part using `+=`. Then just return the variable. The reason it doesnt comply with E122 is possibly because you aren't indenting the following line. – Nick is tired Aug 30 '17 at 16:07
  • What's the full description of E122? – Josh Lee Aug 30 '17 at 16:08
  • 2
    or just tell your ide to increase the line character limit, or ignore that rule all together – Joran Beasley Aug 30 '17 at 16:13
  • 29
    I don't feel its a dupe.. `f` strings are not discussed there. – Ma0 Aug 30 '17 at 16:14
  • 4
    @JoshLee "E122 continuation line missing indentation or outdented main" also why you closed the question? There are no duplicates, its the only one about multiline f-strings – Owlzy Aug 30 '17 at 16:18
  • for the others, this isnt a question about the code, is a question about how to makes multilines f-string looks good and pep-8 compliant without cheesy hacks and non-pythonic styles – Owlzy Aug 30 '17 at 16:20
  • 2
    @Owlzy Isn't the answer the exact same: use parenthesis, not the line-continuation marker? – Nick T Aug 30 '17 at 16:26
  • Also, please clarify if you are talking about a string with linebreaks in it (`\n`) or a multi-line string **literal** (which you don't have, you just added a couple single-line f-string literals). – Nick T Aug 30 '17 at 16:28
  • 10
    This question should have never been closed as a dupe, as the linked 'dupe' ins't a dupe of this question. Stackoverflow power users you know that we have an issue about being *too* aggressive like this, get it re-opened. Casting a re-open vote ASAP. – Urda May 28 '18 at 00:30
  • 1
    @NickT: That may be so but the so called duplicate doesn't mention this and it's not entirely obvious that it'll work. – ChrisWue Jul 23 '18 at 05:03
  • 1
    I agree that this question should not have been marked as a duplicate and closed. The duplicate referenced is not the same. – John Forbes Sep 12 '18 at 01:27
  • The linked question has *ABSOLUTELY NOTHING TO DO* with what was asked here. This one should not have been closed. – Victor Schröder Jan 03 '19 at 16:37

5 Answers5

340

From Style Guide for Python Code:

The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces.

Given this, the following would solve your problem in a PEP-8 compliant way.

return (
    f'{self.date} - {self.time}\n'
    f'Tags: {self.tags}\n'
    f'Text: {self.text}'
)

Python strings will automatically concatenate when not separated by a comma, so you do not need to explicitly call join().

noddy
  • 3,827
  • 3
  • 24
  • 21
  • Wouldn't the `\n` cause problems in Windows? Is there a way to do this without the Unix-specific `\n`? – shinvu Jan 26 '21 at 10:07
  • 5
    No problems on Windows. The `\n` is platform agnostic as Python has had [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) enabled by default since 3.0. – noddy Jan 26 '21 at 16:25
  • 2
    Oh yes, I tried this and it worked. Forgot to update here. Good old Python :'). – shinvu Jan 27 '21 at 11:50
  • 1
    Keep in mind that this is probably less efficient than doing one big f-string, because of extra intermediate (de-)allocations and copying. But it is a good tradeoff for more readable code! – Mattias Wallin Dec 03 '21 at 09:27
  • The `\n` only works if you use `print(...)`, but not, when using return (...). Then `\n` is shown in the output, it is not understood as newline. No need to say, it is still working if you just print the return value. Another idea is in [this answer](https://stackoverflow.com/a/58090379/11154841), – questionto42 Feb 27 '22 at 16:11
  • 1
    What's the recommended style if only some lines contain variables? Should `f` only be added to the lines that have variables or should it be on every line for consistency? – Stevoisiak May 06 '22 at 15:51
  • In my case, the expression inside `{..}` is too long and needs to be broken over many lines. How to do that? – Rafs Nov 15 '22 at 16:32
  • 1
    @MattiasWallin This generates the exact same bytecode as one big f-string would. – Mateen Ulhaq Aug 08 '23 at 12:30
  • 2
    @MateenUlhaq Correct! Tested in Compiler explorer. [This answer](https://stackoverflow.com/questions/34174539/python-string-literal-concatenation) suggests it is concatenated during parsing. [Reference manual](https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation) says it is equivalent to concatenated version. – Mattias Wallin Aug 09 '23 at 09:56
85

I think it would be

return f'''{self.date} - {self.time},
Tags: {self.tags},
Text: {self.text}'''
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • 1
    now that its closed i can delete this answer if people think i should ... but its not covered in the dupe answer :/\ – Joran Beasley Aug 30 '17 at 16:11
  • 11
    but this kind of multiline defeats the purpose of f-strings and indentation Also I dont feel like this is the right use of the triple quote strings, feels like an hack – Owlzy Aug 30 '17 at 16:23
  • 4
    While this answer does replicate the OP's intent, I feel like @noddy's answer is better. This answer *just happens* to be correct because the OP also wanted multi-line in the output. If you wanted the output to have a different layout than the source code, triple quotes are not the way to go. – Mike Williamson Nov 13 '19 at 09:47
  • 1
    Looks nice, but creates problems with indentation and code layout. – DZet Mar 31 '21 at 11:56
  • 2
    mix this with the builtin `inspect.cleandoc` function and you'll have a good day :) – Zack Plauché May 14 '21 at 06:31
  • 1
    Yeah, I prefer this solution combined with `inspect.cleandoc` as well. – Person Aug 09 '22 at 08:04
  • @Owlzy can you explain what you mean how "this kind of multiline defeats the purpose of f-strings and indentation"? And I'm adding a link for @Zack Plauche's reference to [`inspect.cleandoc`](https://github.com/jmurty/til/blob/main/python/use-multi-line-text-blocks-with-inspect-cleandoc.md) which "cleans up" leading indentations/spaces – Nate Anderson May 11 '23 at 20:41
  • @NateAnderson my answer is more than 5 years old but the answer to your message was given by Mike Williamson 3 years ago in the comment directly under mine. Potentially I didn't need to have the output text on multiple lines, rendering this solution useless and off topic – Owlzy May 12 '23 at 07:37
  • OK @Owlzy, I like Mike's comment; I can see why [the accepted answer](https://stackoverflow.com/a/54950733/1175496) is better than this answer, esp considering [PEP-8](https://stackoverflow.com/questions/1874592/how-to-write-very-long-string-that-conforms-with-pep8-and-prevent-e501); if your OP didn't ask about a string **with linebreaks**, then a **multiline** aka "triple-quote" string (seen in this answer) wouldn't help? I'm not sure this (or [other highly-voted answers suggesting multiline strings](https://stackoverflow.com/a/58090379/1175496)) is "useless and off topic" as you say.... – Nate Anderson May 12 '23 at 23:04
  • 1
    Maybe a multiline string "feels like an hack" because multiline are often used as Python [docstrings](https://peps.python.org/pep-0257/#multi-line-docstrings), instead of string values stored in variables?? Anyway I'm glad to learn **f-strings and multiline strings can work together**, *and* that parentheses are "the preferred way of wrapping long lines". I wonder which is the [most performant](https://stackoverflow.com/questions/45965007/multiline-f-string-in-python/45965132?noredirect=1#comment124116413_54950733) (maybe premature optimization). I'm glad I found this Q&A, even 5 years late. – Nate Anderson May 12 '23 at 23:12
77

You can use either triple single quotation marks or triple double quotation marks, but put an f at the beginning of the string:

Triple Single Quotes

return f'''{self.date} - {self.time},
Tags:' {self.tags},
Text: {self.text}'''

Triple Double Quotes

return f"""{self.date} - {self.time},
Tags:' {self.tags},
Text: {self.text}"""

Notice that you don't need to use "\n" because you are using a multiple-line string.

lmiguelvargasf
  • 63,191
  • 45
  • 217
  • 228
  • 5
    Good point about "\n" - and here is [another reference](https://automatetheboringstuff.com/chapter6/) that also explains it's not necessary for multiline Strings (in section, "Multiline Strings with Triple Quotes"). – cellepo May 18 '21 at 17:33
29

As mentioned by @noddy, the approach also works for variable assignment expression:

var1 = "foo"
var2 = "bar"
concat_var = (f"First var is: {var1}"
              f" and in same line Second var is: {var2}")
print(concat_var)

should give you:

First var is: foo and in same line Second var is: bar
codarrior
  • 524
  • 5
  • 5
7

You can mix the multiline quoting styles and regular strings and f-strings:

foo = 'bar'
baz = 'bletch'
print(f'foo is {foo}!\n',
      'bar is bar!\n',
      f"baz is {baz}!\n",
      '''bletch
      is
      bletch!''')

Prints this (note the indentation):

foo is bar!
 bar is bar!
 baz is bletch!
 bletch
      is
      bletch!
Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47