如何将 Python 代码保持在 80 个字符以下而不使其变得丑陋?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4841226/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:46:45  来源:igfitidea点击:

How Do I Keep Python Code Under 80 Chars Without Making It Ugly?

python

提问by Eli

This is a question that keeps recurring in all of my programming, python and otherwise. I really like to keep my code under 80 chars if at all possible/not horribly ugly. In a language like Perl, this isn't too hard since white space doesn't matter. In Python, where it does, I wind up knocking my head against the wall more often than I'd like to be trying to think of a "nice" way of splitting up my long lines. So, Code gurus, how do you do it? Any general strategies you can inform me of?

这是一个在我所有的编程、python 和其他方面不断重复出现的问题。如果可能的话/不是非常丑陋,我真的很喜欢将我的代码保持在 80 个字符以下。在像 Perl 这样的语言中,这并不太难,因为空格无关紧要。在 Python 中,在它确实如此的地方,我最终将头撞到墙上的次数比我想想出一种分割长行的“好”方式的次数要多。那么,代码大师,你是怎么做到的?您可以告诉我任何一般策略吗?

A particular problem I'm dealing with right now is:

我现在正在处理的一个特殊问题是:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj, self.user1, self.user2)

When I naturally try to cut this off in Python, the only half decent way available to me seems to be:

当我自然地尝试在 Python 中切断它时,我唯一可用的半体面的方法似乎是:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj,
                                                          self.user1
                                                          self.user2)

It doesn't look that bad, I guess, but it takes up three lines, which is just completely unnecessary. There must be a better way, no?

我想它看起来并没有那么糟糕,但它占用了三行,这完全没有必要。一定有更好的方法,不是吗?

Note: I know there are those of you who don't like 80 chars a line and have created your own limits. I understand the motivation behind this and respect it, but 80 chars is my preferred limit. Please don't take up space trying to convince me to go over to 120 or some such here.

注意:我知道有些人不喜欢一行 80 个字符并创建了自己的限制。我理解这背后的动机并尊重它,但 80 个字符是我的首选限制。请不要占用空间试图说服我转到 120 或一些类似的地方。

采纳答案by Johnsyweb

The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation. Make sure to indent the continued line appropriately. The preferred place to break around a binary operator is afterthe operator, not before it.

包装长行的首选方法是在括号、方括号和大括号内使用 Python 的隐含行续行。通过将表达式括在括号中,可以将长行分成多行。这些应该优先于使用反斜杠进行行延续。确保适当缩进连续行。打破二元运算符的首选位置是运算符之后,而不是在它之前。

PEP 8 Style Guide for Python Code(follow link for examples).

PEP 8 Python 代码风格指南(点击链接查看示例)。

回答by Noctis Skytower

Try shortening your names if you have that option. Otherwise you can use the \character to continue your lines onto the next line (along with other similar constructs such as what you mentioned up above).

如果你有这个选择,试着缩短你的名字。否则,您可以使用该\字符将您的行延续到下一行(以及其他类似的结构,例如您上面提到的内容)。

回答by Michael Kent

self.SomeLongLongName = SomeLongLongName.\
    SomeLongLongName(some_obj, self.user1, self.user2)

'\' is your friend. Of course, you already know that you can split lines in an argument list at commas, without using '\'. Also, if you have long strings:

'\' 是你的朋友。当然,您已经知道可以用逗号分隔参数列表中的行,而无需使用“\”。另外,如果你有长字符串:

myLongString = "This is a really long string that is going to be longer than 80 characters so oh my what do I do to make this work out?"

becomes:

变成:

myLongString = "This is a really long string that is going to be longer than"\
    " 80 characters so oh my what do I do to make this work out?"

This works because Python will combine adjacent string literals, ignoring whitespace between the adjacent literal strings.

这是有效的,因为 Python 将组合相邻的字符串文字,忽略相邻文字字符串之间的空格。

回答by davidg

Your code style seems to insist that if you break a line inside a parenthesis, lines below need to line up with it:

你的代码风格似乎坚持如果你在括号内换行,下面的行需要与它对齐:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj,
                                                          self.user1
                                                          self.user2)

If you are willing to drop this requirement, you can format the code as follows, where continued lines have a fixed double indent:

如果您愿意放弃此要求,可以按如下方式格式化代码,其中连续行具有固定的双缩进:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(
        some_obj, self.user1, self.user2)

This avoids writing code down the right-hand margin on the page, and is very readable once you are used to it. It also has the benefit that if you modify the name of "SomeLongLongName", you don't have to re-indent all of the following lines. A longer example would be as follows:

这避免了在页面的右侧边距上写下代码,并且一旦您习惯了它就非常易读。它还具有以下好处:如果您修改“SomeLongLongName”的名称,则不必重新缩进以下所有行。一个更长的例子如下:

if SomeLongLongName.SomeLongLongName(
        some_obj, self.user1, self.user2):
    foo()
else:     
    bar()

The double indent for continued lines allows you to visually separate them from lines indented because they are in an ifor elseblock.

连续行的双缩进允许您在视觉上将它们与因位于iforelse块中而缩进的行分开。

As others have noted, using shorted names also helps, but this isn't always possible (such as when using an external API).

正如其他人所指出的,使用简称也有帮助,但这并不总是可行的(例如在使用外部 API 时)。

回答by steveha

I second Michael Kent's answer (and I upvoted it).

我支持迈克尔肯特的回答(我赞成)。

But also, you should read "PEP 8" and absorb its lessons.

而且,您应该阅读“PEP 8”并吸取其中的教训。

http://www.python.org/dev/peps/pep-0008/

http://www.python.org/dev/peps/pep-0008/

But Python, with its namespaces, powerful features, and object-oriented classes, should let you use conveniently short names for things.

但是 Python 凭借其命名空间、强大的功能和面向对象的类,应该让您可以方便地为事物使用短名称。

In C, you need to use long identifiers in many cases because names need to be unique within a given scope. Thus:

在 C 中,在许多情况下您需要使用长标识符,因为名称在给定的范围内需要是唯一的。因此:

char *StringFromInt(int x);
char *StringFromFloat(float x);
char *StringFromUnsigned(unsigned int x);

char *str_temp = strdup(StringFromUnsigned(foo_flags));

In Python, all of these would be the builtin str():

在 Python 中,所有这些都是内置的str()

temp = str(foo_flags)

In C++ you have classes and name spaces, so you should be able to use object-oriented features as in Python, but in C you need globally unique names, so you often have to do stuff like this:

在 C++ 中,您有类和名称空间,因此您应该能够像在 Python 中一样使用面向对象的功能,但在 C 中,您需要全局唯一的名称,因此您通常必须执行以下操作:

typedef struct s_foo
{
   // struct members go here
} FOO;

FooAdd();
FooSubtract();
StringFromFoo();

In Python, you should either add member functions, or overload operators, as appropriate:

在 Python 中,您应该根据需要添加成员函数或重载运算符:

class Foo(object):
    def __init__(self):
        # member variables initialized here
    def add(self, x):
        # add x to a Foo
    def subtract(self, x):
        # subtract x from a Foo
    def __str___(self):
        # return a string that represents a foo

f = Foo()
f.add(x)
f.sub(y)
# the following two both use __str__()
temp = str(f)
print(f)

You may also favor really long variable names for self-documenting purposes. I prefer terseness:

出于自我记录的目的,您也可能喜欢使用非常长的变量名称。我更喜欢简洁:

import math

class Circle(object):
    """\
Circle: a class representing a circle in a plane.
Includes the following member functions:
    area() -- return the area of the circle"""
    def __init__(self, center=Point([0, 0]), radius=0.0):
        """\
Circle(center, radius)
center must be an instance of class Point() or convertible to Point()
radius must be an int or float and must not be negative"""
        if radius < 0:
            raise ValueError("radius must be >= 0")
        self.center = Point(center)
        self.radius = float(radius)
    def area(self):
        "returns area as a float."
         return math.pi * self.radius ** 2

c = Circle([23, 45], 0.5)
print(c.area())


class CircleGraphicsObject(object):
    def __init__(self, CenterOfTheCircle, RadiusOfTheCircle):
        # init code goes here
    def AreaOfTheCircle(self):
        return math.pi * self.RadiusOfTheCircle ** 2

CircleInstance = CircleGraphicsObject(PointObject([23, 45]), 0.5)
print(CircleInstance.AreaOfTheCircle())

I strongly prefer the first, terse style to the second. As per PEP 8, I like all-lower-case variable names (such as cfor the Circleinstance). In Python, it is also generally recommended to use "Duck Typing" like I did in the terse class: if you want the radius to be a float, then coerce it to a floatin __init__()rather than checking its type. Likewise, rather than checking to see if you were passed a Pointinstance, just coerce whatever you get to a Point. You are letting Point.__init__()raise an exception if the argument makes no sense as a Point; there is no need for an extra check in Circle.__init__(). Also, your Point.__init__()function can explicitly check to see if you passed it an instance of Pointand return the instance unchanged, if it is really expensive to init a Point. (In this example, a Pointis really just a pair of values, so it's probably fast enough to just re-create the point and you don't need the check.)

我强烈喜欢第一种简洁的风格而不是第二种。按PEP 8,I像全小写变量名(如cCircle实例)。在 Python 中,通常也建议使用“Duck Typing”,就像我在 terse 类中所做的那样:如果您希望半径为浮点数,则将其强制为floatin__init__()而不是检查其类型。同样,与其检查您是否通过了一个Point实例,只需将您获得的任何内容强制转换为Point. Point.__init__()如果参数作为Point;没有意义,则您将引发异常。无需额外办理入住手续Circle.__init__()。此外,您的Point.__init__()函数可以显式检查您是否向它传递了一个Point并返回不变的实例,如果初始化 a 真的很昂贵Point。(在这个例子中, aPoint实际上只是一对值,所以它可能足够快,只需重新创建点并且您不需要检查。)

You might notice the odd way I did the multi-line triple-quoted string. Because of the indenting rules in Python, I needed to indent the triple-quoted string, but I don't want to indent the lines of the string because the indent would be part of the string. Really I want all of the multiple lines to be at the left margin, so I can clearly see how long those lines are getting (and make sure they are all 79 chars or shorter). So I use the backslash escape to allow the first line of the multi-line string to be at the left margin with the other lines, without inserting a newline at the beginning of the multi-line string.

您可能会注意到我处理多行三引号字符串的奇怪方式。由于 Python 中的缩进规则,我需要缩进三引号字符串,但我不想缩进字符串的行,因为缩进将成为字符串的一部分。我真的希望所有的多行都在左边距,所以我可以清楚地看到这些行有多长(并确保它们都是 79 个字符或更短)。所以我使用反斜杠转义来允许多行字符串的第一行与其他行位于左边距,而不在多行字符串的开头插入换行符。

Anyway, the terser style means your variable names and such are easier to type, and it is easier to fit your lines in the 79-column limit recommended by PEP 8.

无论如何,更简洁的风格意味着你的变量名等更容易输入,并且更容易让你的行符合 PEP 8 推荐的 79 列限制。

It wouldn't even be completely horrible to use internal member names that are one letter long, in a class as simple as this. With only two members, you could pretty well use .cfor the center member and .rfor the radius. But that doesn't scale up well, and .centerand .radiusare still easy to type and easy to remember.

在像这样简单的类中使用一个字母长的内部成员名称甚至不会完全可怕。只有两个成员,你可以很好地使用.c中心成员和.r半径。但是,这并不按比例增长,以及.center.radius仍然容易输入易记。

It's also a very good idea to put informative docstrings. You can use somewhat terse names, but have longer explanations in the docstring.

放置信息性文档字符串也是一个很好的主意。您可以使用稍微简洁的名称,但在文档字符串中有更长的解释。

class Foo(object):
    # init goes here
    def area(self):
        "returns area as a float."
         return self.area

class VerboseFoo(object):
    # init goes here
    def AreaAsFloat(self):
        return self.FloatAreaValue

Namespaces are great. Notice how clear it is when we use math.pi; you know it is the math constant, and you could have some local variable pi(for "Program Index" perhaps) and it does not collide with the math constant.

命名空间很棒。请注意,当我们使用math.pi; 你知道它是数学常数,你可以有一些局部变量pi(也许是“程序索引”)并且它不会与数学常数发生冲突。

回答by Derek Litz

Some people were citing the Rectangle class as a poor example. This example in the pep8 is notthe only way to do this.

有些人引用 Rectangle 类作为一个糟糕的例子。pep8 中的这个示例并不是执行此操作的唯一方法。

Original:

原来的:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if (width == 0 and height == 0 and
            color == 'red' and emphasis == 'strong' or
            highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so -- values are %s, %s" %
                             (width, height))
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)

This is how Iwould write it.

这就是要写的方式。

class Rectangle(Blob):

    def __init__(self, width, height, color='black', emphasis=None,
            highlight=0):
        if (width == 0 and height == 0 and color == 'red' and
                emphasis == 'strong' or highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or 
                emphasis is None):
            msg = "I don't think so -- values are %s, %s" % (width, height)     
            raise ValueError(msg)
        Blob.__init__(self, width, height, color, emphasis, highlight)

The reason being is:

原因是:

  • Additional indentation to line up with '(' is a waste of time if your editor isn't doing it for you and harder to read since there is so much leading white space IMO.
  • I try to break as late as possible unless there is a compelling reason in the code logic.
  • Lining up with the '(' in this case created the exact same indentation level as the next line... very bad coincidence! Double indenting continuation lines solves this problem.
  • I prefer avoidance if the reason for having to use line continuation is trying to do too much on one line. The example here is the ValueError where they are formatting using the string format operator. I set msg instead. (Note: Format Strings using the format method are preferred, and %is deprecated since 3.1).
  • 如果您的编辑器不是为您做的并且难以阅读,那么与 '(' 对齐的额外缩进是浪费时间,因为 IMO 有太多领先的空白。
  • 除非代码逻辑中有令人信服的理由,否则我会尝试尽可能晚地中断。
  • 在这种情况下,与 '(' 对齐会创建与下一行完全相同的缩进级别......非常巧合!双缩进续行解决了这个问题。
  • 如果不得不使用行延续的原因是试图在一行上做太多事情,我更喜欢避免。此处的示例是 ValueError,它们使用字符串格式运算符进行格式化。我改为设置 msg。(注意:使用 format 方法格式化字符串是首选,%自 3.1 起已弃用)。

回答by crizCraig

I find myself using more and more intermediate variables, that not only help stay within 80 characters, but make the code more readable by giving things descriptive names like:

我发现自己使用了越来越多的中间变量,这不仅有助于保持在 80 个字符以内,而且通过给出如下描述性名称使代码更具可读性:

old_name = 'reallylonguglypath/to/current/file.foo'
new_name = 'evenmoreuglylong/to/new/desination/for/file.foo' 
os.rename(old_name, new_name)

rather than:

而不是:

os.rename("reallylonguglypath/to/current/file.foo",
              "evenmoreuglylong/to/new/desination/for/file.foo")

You can do this with long module and class names too

您也可以使用长模块和类名来做到这一点

method = SomeLongClassName.SomeLongMethodName
self.SomeLongLongName = method(some_obj, self.user1, self.user2)