如何编写符合PEP8的很长的字符串并防止E501

问题:如何编写符合PEP8的很长的字符串并防止E501

正如PEP8建议将python程序的规则保持在80列以下,我该如何使用长字符串来遵守该规则,即

s = "this is my really, really, really, really, really, really, really long string that I'd like to shorten."

我如何将其扩展到以下行,即

s = "this is my really, really, really, really, really, really" + 
    "really long string that I'd like to shorten."

As PEP8 suggests keeping below the 80 column rule for your python program, how can I abide to that with long strings, i.e.

s = "this is my really, really, really, really, really, really, really long string that I'd like to shorten."

How would I go about expanding this to the following line, i.e.

s = "this is my really, really, really, really, really, really" + 
    "really long string that I'd like to shorten."

回答 0

隐式串联可能是最干净的解决方案:

s = "this is my really, really, really, really, really, really," \
    " really long string that I'd like to shorten."

编辑在反思我同意,托德的建议,请使用括号,而不是续行是所有他给出的理由更好。我唯一的犹豫是将带括号的字符串与元组混淆是相对容易的。

Implicit concatenation might be the cleanest solution:

s = "this is my really, really, really, really, really, really," \
    " really long string that I'd like to shorten."

Edit On reflection I agree that Todd’s suggestion to use brackets rather than line continuation is better for all the reasons he gives. The only hesitation I have is that it’s relatively easy to confuse bracketed strings with tuples.


回答 1

另外,由于相邻的字符串常量是自动连接的,因此您也可以这样编码:

s = ("this is my really, really, really, really, really, really, "  
     "really long string that I'd like to shorten.")

请注意没有加号,我在示例格式之后添加了额外的逗号和空格。

我个人不喜欢反斜杠,并且我记得在某处读到它的使用实际上已被弃用,而倾向于这种更明确的形式。记住“显式胜于隐式”。

我认为反斜杠不太清楚,用处也不大,因为这实际上是在换行符的转义。如果有必要在行末尾添加注释。可以使用串联的字符串常量来做到这一点:

s = ("this is my really, really, really, really, really, really, " # comments ok
     "really long string that I'd like to shorten.")

我使用Google搜索“ python行长”来返回PEP8链接作为第一个结果,同时还链接到另一个有关该主题的StackOverflow帖子:“ 为什么Python PEP-8应该指定最大行长为79个字符?

另一个很好的搜索词组是“ python行连续”。

Also, because neighboring string constants are automatically concatenated, you can code it like this too:

s = ("this is my really, really, really, really, really, really, "  
     "really long string that I'd like to shorten.")

Note no plus sign, and I added the extra comma and space that follows the formatting of your example.

Personally I don’t like the backslashes, and I recall reading somewhere that its use is actually deprecated in favor of this form which is more explicit. Remember “Explicit is better than implicit.”

I consider the backslash to be less clear and less useful because this is actually escaping the newline character. It’s not possible to put a line end comment after it if one should be necessary. It is possible to do this with concatenated string constants:

s = ("this is my really, really, really, really, really, really, " # comments ok
     "really long string that I'd like to shorten.")

I used a Google search of “python line length” which returns the PEP8 link as the first result, but also links to another good StackOverflow post on this topic: “Why should Python PEP-8 specify a maximum line length of 79 characters?

Another good search phrase would be “python line continuation”.


回答 2

我认为您的问题中最重要的词是“建议”。

编码标准很有趣。通常,他们提供的指南在编写时有很好的基础(例如,大多数终端无法在一行上显示> 80个字符),但是随着时间的推移,它们在功能上已过时,但仍然严格遵守。我想您在这里需要做的是权衡“打破”该特定建议与代码的可读性和可维护性的相对优点。

抱歉,这不能直接回答您的问题。

I think the most important word in your question was “suggests”.

Coding standards are funny things. Often the guidance they provide has a really good basis when it was written (e.g. most terminals being unable to show > 80 characters on a line), but over time they become functionally obsolete, but still rigidly adhered to. I guess what you need to do here is weigh up the relative merits of “breaking” that particular suggestion against the readability and mainatinability of your code.

Sorry this doesn’t directly answer your question.


回答 3

您丢失了一个空格,并且可能需要换行符,即。一个\

s = "this is my really, really, really, really, really, really" +  \
    " really long string that I'd like to shorten."

甚至:

s = "this is my really, really, really, really, really, really"  \
    " really long string that I'd like to shorten."

Parens也可以代替行继续,但是您可能会冒险有人认为您打算创建一个元组而忘记了逗号。举个例子:

s = ("this is my really, really, really, really, really, really"
    " really long string that I'd like to shorten.")

与:

s = ("this is my really, really, really, really, really, really",
    " really long string that I'd like to shorten.")

使用Python的动态类型,代码可能会以任何一种方式运行,但是会产生与您不想要的结果相同的错误结果。

You lost a space, and you probably need a line continuation character, ie. a \.

s = "this is my really, really, really, really, really, really" +  \
    " really long string that I'd like to shorten."

or even:

s = "this is my really, really, really, really, really, really"  \
    " really long string that I'd like to shorten."

Parens would also work instead of the line continuation, but you risk someone thinking you intended to have a tuple and had just forgotten a comma. Take for instance:

s = ("this is my really, really, really, really, really, really"
    " really long string that I'd like to shorten.")

versus:

s = ("this is my really, really, really, really, really, really",
    " really long string that I'd like to shorten.")

With Python’s dynamic typing, the code may run either way, but produce incorrect results with the one you didn’t intend.


回答 4

反斜杠:

s = "this is my really, really, really, really, really, really" +  \
    "really long string that I'd like to shorten."

或包裹在括号中:

s = ("this is my really, really, really, really, really, really" + 
    "really long string that I'd like to shorten.")

Backslash:

s = "this is my really, really, really, really, really, really" +  \
    "really long string that I'd like to shorten."

or wrap in parens:

s = ("this is my really, really, really, really, really, really" + 
    "really long string that I'd like to shorten.")

回答 5

这些都是很好的答案,但是我找不到能帮助我编辑“隐式连接”字符串的编辑器插件,因此我编写了一个程序包使它更容易使用。

在pip(安装段落)上,如果有人在徘徊这个旧线程想要将其签出。格式化html格式的多行字符串(压缩空格,为新段落添加两个换行符,不必担心行之间的空格)。

from paragraphs import par


class SuddenDeathError(Exception):
    def __init__(self, cause: str) -> None:
        self.cause = cause

    def __str__(self):
        return par(
            f""" Y - e - e - e - es, Lord love you! Why should she die of
            {self.cause}? She come through diphtheria right enough the year
            before. I saw her with my own eyes. Fairly blue with it, she
            was. They all thought she was dead; but my father he kept ladling
            gin down her throat till she came to so sudden that she bit the bowl
            off the spoon. 

            What call would a woman with that strength in her have to die of
            {self.cause}? What become of her new straw hat that should have
            come to me? Somebody pinched it; and what I say is, them as pinched
            it done her in."""
        )


raise SuddenDeathError("influenza")

变成…

__main__.SuddenDeathError: Y - e - e - e - es, Lord love you! Why should she die of influenza? She come through diphtheria right enough the year before. I saw her with my own eyes. Fairly blue with it, she was. They all thought she was dead; but my father he kept ladling gin down her throat till she came to so sudden that she bit the bowl off the spoon.

What call would a woman with that strength in her have to die of influenza? What become of her new straw hat that should have come to me? Somebody pinched it; and what I say is, them as pinched it done her in.

一切都轻松地与(Vim)’gq’对齐

These are all great answers, but I couldn’t find an editor plugin that would help me with editing “implicitly concatenated” strings, so I wrote a package to make it easier on me.

On pip (install paragraphs) if whoever’s wandering this old thread would like to check it out. Formats multi-line strings the way html does (compress whitespace, two newlines for a new paragraph, no worries about spaces between lines).

from paragraphs import par


class SuddenDeathError(Exception):
    def __init__(self, cause: str) -> None:
        self.cause = cause

    def __str__(self):
        return par(
            f""" Y - e - e - e - es, Lord love you! Why should she die of
            {self.cause}? She come through diphtheria right enough the year
            before. I saw her with my own eyes. Fairly blue with it, she
            was. They all thought she was dead; but my father he kept ladling
            gin down her throat till she came to so sudden that she bit the bowl
            off the spoon. 

            What call would a woman with that strength in her have to die of
            {self.cause}? What become of her new straw hat that should have
            come to me? Somebody pinched it; and what I say is, them as pinched
            it done her in."""
        )


raise SuddenDeathError("influenza")

becomes …

__main__.SuddenDeathError: Y - e - e - e - es, Lord love you! Why should she die of influenza? She come through diphtheria right enough the year before. I saw her with my own eyes. Fairly blue with it, she was. They all thought she was dead; but my father he kept ladling gin down her throat till she came to so sudden that she bit the bowl off the spoon.

What call would a woman with that strength in her have to die of influenza? What become of her new straw hat that should have come to me? Somebody pinched it; and what I say is, them as pinched it done her in.

Everything lines up easily with (Vim) ‘gq’


回答 6

使用a \可以将语句扩展到多行:

s = "this is my really, really, really, really, really, really" + \
"really long string that I'd like to shorten."

应该管用。

With a \ you can expand statements to multiple lines:

s = "this is my really, really, really, really, really, really" + \
"really long string that I'd like to shorten."

should work.


回答 7

我倾向于使用一些此处未提及的方法来指定大字符串,但这是针对非常特定的场景的。YMMV …

  • 多行文本,通常带有格式化标记(不是您所要的,但仍然有用):

    error_message = '''
    I generally like to see how my helpful, sometimes multi-line error
    messages will look against the left border.
    '''.strip()
  • 通过您喜欢的任何字符串插值方法逐段增加变量:

    var = 'This is the start of a very,'
    var = f'{var} very long string which could'
    var = f'{var} contain a ridiculous number'
    var = f'{var} of words.'
  • 从文件中读取。PEP-8不会限制文件中字符串的长度;只是您的代码行。:)

  • 使用蛮力或您的编辑器使用换行符将字符串拆分为可换行,然后删除所有换行符。(类似于我列出的第一种技术):

    foo = '''
    agreatbigstringthatyoudonotwanttohaveanyne
    wlinesinbutforsomereasonyouneedtospecifyit
    verbatimintheactualcodejustlikethis
    '''.replace('\n', '')

I tend to use a couple of methods not mentioned here for specifying large strings, but these are for very specific scenarios. YMMV…

  • Multi-line blobs of text, often with formatted tokens (not quite what you were asking, but still useful):

    error_message = '''
    I generally like to see how my helpful, sometimes multi-line error
    messages will look against the left border.
    '''.strip()
    
  • Grow the variable piece-by-piece through whatever string interpolation method you prefer:

    var = 'This is the start of a very,'
    var = f'{var} very long string which could'
    var = f'{var} contain a ridiculous number'
    var = f'{var} of words.'
    
  • Read it from a file. PEP-8 doesn’t limit the length of strings in a file; just the lines of your code. :)

  • Use brute-force or your editor to split the string into managaeble lines using newlines, and then remove all newlines. (Similar to the first technique I listed):

    foo = '''
    agreatbigstringthatyoudonotwanttohaveanyne
    wlinesinbutforsomereasonyouneedtospecifyit
    verbatimintheactualcodejustlikethis
    '''.replace('\n', '')
    

回答 8

可用选项:

  • 反斜杠"foo" \ "bar"
  • 加号后跟反斜杠"foo" + \ "bar"
  • 括号
    • ("foo" "bar")
    • 加号的括号("foo" + "bar")
    • PEP8,E502:括号之间的反斜杠是多余的

避免

避免用逗号括起来:("foo", "bar")定义一个元组。


>>> s = "a" \
... "b"
>>> s
'ab'
>>> type(s)
<class 'str'>
>>> s = "a" + \
... "b"
>>> s
'ab'
>>> type(s)
<class 'str'>
>>> s = ("a"
... "b")
>>> type(s)
<class 'str'>
>>> print(s)
ab
>>> s = ("a",
... "b")
>>> type(s)
<class 'tuple'>
>>> s = ("a" + 
... "b")
>>> type(s)
<class 'str'>
>>> print(s)
ab
>>> 

Available options:

  • backslash: "foo" \ "bar"
  • plus sign followed by backslash: "foo" + \ "bar"
  • brackets:
    • ("foo" "bar")
    • brackets with plus sign: ("foo" + "bar")
    • PEP8, E502: the backslash is redundant between brackets

Avoid

Avoid brackets with comma: ("foo", "bar") which defines a tuple.


>>> s = "a" \
... "b"
>>> s
'ab'
>>> type(s)
<class 'str'>
>>> s = "a" + \
... "b"
>>> s
'ab'
>>> type(s)
<class 'str'>
>>> s = ("a"
... "b")
>>> type(s)
<class 'str'>
>>> print(s)
ab
>>> s = ("a",
... "b")
>>> type(s)
<class 'tuple'>
>>> s = ("a" + 
... "b")
>>> type(s)
<class 'str'>
>>> print(s)
ab
>>> 

回答 9

如果您必须插入一个长字符串文字并希望flake8关闭,则可以使用它的关闭指令。例如,在测试例程中,我定义了一些伪造的CSV输入。我发现将其分割成行的更多行会造成极大的混乱,因此我决定添加# noqa: E501以下内容:

csv_test_content = """"STATION","DATE","SOURCE","LATITUDE","LONGITUDE","ELEVATION","NAME","REPORT_TYPE","CALL_SIGN","QUALITY_CONTROL","WND","CIG","VIS","TMP","DEW","SLP","AA1","AA2","AY1","AY2","GF1","MW1","REM"
"94733099999","2019-01-03T22:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","050,1,N,0010,1","22000,1,9,N","025000,1,9,9","+0260,1","+0210,1","99999,9","24,0000,9,1",,"0,1,02,1","0,1,02,1","01,99,1,99,9,99,9,99999,9,99,9,99,9","01,1","SYN05294733 11/75 10502 10260 20210 60004 70100 333 70000="
"94733099999","2019-01-04T04:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","090,1,N,0021,1","22000,1,9,N","025000,1,9,9","+0378,1","+0172,1","99999,9","06,0000,9,1",,"0,1,02,1","0,1,02,1","03,99,1,99,9,99,9,99999,9,99,9,99,9","03,1","SYN04294733 11/75 30904 10378 20172 60001 70300="
"94733099999","2019-01-04T22:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","290,1,N,0057,1","99999,9,9,N","020000,1,9,9","+0339,1","+0201,1","99999,9","24,0000,9,1",,"0,1,02,1","0,1,02,1",,"02,1","SYN05294733 11970 02911 10339 20201 60004 70200 333 70000="
"94733099999","2019-01-05T22:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","200,1,N,0026,1","99999,9,9,N","000100,1,9,9","+0209,1","+0193,1","99999,9","24,0004,3,1",,"1,1,02,1","1,1,02,1","08,99,1,99,9,99,9,99999,9,99,9,99,9","51,1","SYN05294733 11/01 82005 10209 20193 69944 75111 333 70004="
"94733099999","2019-01-08T04:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","070,1,N,0026,1","22000,1,9,N","025000,1,9,9","+0344,1","+0213,1","99999,9","06,0000,9,1",,"2,1,02,1","2,1,02,1","04,99,1,99,9,99,9,99999,9,99,9,99,9","02,1","SYN04294733 11/75 40705 10344 20213 60001 70222="
"""  # noqa: E501

If you must insert a long string literal and want flake8 to shut up, you can use it’s shutting up directives. For example, in a testing routine I defined some fake CSV input. I found that splitting it over more lines that it had rows would be mightily confusing, so I decided to add a # noqa: E501 as follows:

csv_test_content = """"STATION","DATE","SOURCE","LATITUDE","LONGITUDE","ELEVATION","NAME","REPORT_TYPE","CALL_SIGN","QUALITY_CONTROL","WND","CIG","VIS","TMP","DEW","SLP","AA1","AA2","AY1","AY2","GF1","MW1","REM"
"94733099999","2019-01-03T22:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","050,1,N,0010,1","22000,1,9,N","025000,1,9,9","+0260,1","+0210,1","99999,9","24,0000,9,1",,"0,1,02,1","0,1,02,1","01,99,1,99,9,99,9,99999,9,99,9,99,9","01,1","SYN05294733 11/75 10502 10260 20210 60004 70100 333 70000="
"94733099999","2019-01-04T04:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","090,1,N,0021,1","22000,1,9,N","025000,1,9,9","+0378,1","+0172,1","99999,9","06,0000,9,1",,"0,1,02,1","0,1,02,1","03,99,1,99,9,99,9,99999,9,99,9,99,9","03,1","SYN04294733 11/75 30904 10378 20172 60001 70300="
"94733099999","2019-01-04T22:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","290,1,N,0057,1","99999,9,9,N","020000,1,9,9","+0339,1","+0201,1","99999,9","24,0000,9,1",,"0,1,02,1","0,1,02,1",,"02,1","SYN05294733 11970 02911 10339 20201 60004 70200 333 70000="
"94733099999","2019-01-05T22:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","200,1,N,0026,1","99999,9,9,N","000100,1,9,9","+0209,1","+0193,1","99999,9","24,0004,3,1",,"1,1,02,1","1,1,02,1","08,99,1,99,9,99,9,99999,9,99,9,99,9","51,1","SYN05294733 11/01 82005 10209 20193 69944 75111 333 70004="
"94733099999","2019-01-08T04:00:00","4","-32.5833333","151.1666666","45.0","SINGLETON STP, AS","FM-12","99999","V020","070,1,N,0026,1","22000,1,9,N","025000,1,9,9","+0344,1","+0213,1","99999,9","06,0000,9,1",,"2,1,02,1","2,1,02,1","04,99,1,99,9,99,9,99999,9,99,9,99,9","02,1","SYN04294733 11/75 40705 10344 20213 60001 70222="
"""  # noqa: E501

回答 10

我过去使用过textwrap.dedent。这有点麻烦,所以我现在更喜欢连续行,但是如果您真的想要块缩进,我认为这很棒。

示例代码(其中修剪将去除带有切片的第一个“ \ n”):

import textwrap as tw
x = """\
       This is a yet another test.
       This is only a test"""
print(tw.dedent(x))

说明:

dedent根据换行之前第一行文本中的空格来计算缩进。如果您想对其进行调整,则可以使用该re模块轻松地重新实现它。

此方法有局限性,因为很长的行可能仍然比您想要的更长,在这种情况下,其他将字符串连接起来的方法更合适。

I’ve used textwrap.dedent in the past. It’s a little cumbersome so I prefer line continuations now but if you really want the block indent, I think this is great.

Example Code (where the trim is to get rid of the first ‘\n’ with a slice):

import textwrap as tw
x = """\
       This is a yet another test.
       This is only a test"""
print(tw.dedent(x))

Explanation:

dedent calculates the indentation based on the white space in the first line of text before a new line. If you wanted to tweak it, you could easily reimplement it using the re module.

This method has limitations in that very long lines may still be longer than you want in which case other methods that concatenate strings is more suitable.