如何保持Python代码不超过80个字符而又不丑?
这是一个在我的所有编程,python和其他方面不断重复的问题。 我真的很喜欢把我的代码保存在80个字符以内,如果可能/不可怕的话。 在像Perl这样的语言中,这并不难,因为空格并不重要。 在Python中,我经常把头撞到墙上,而不是想用“好”的方式来分割我的长线。 所以,代码大师,你是怎么做到的? 任何一般的策略,你可以通知我?
我正在处理的一个特殊问题是:
self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj, self.user1, self.user2)
当我自然地试图用Python来切断这一切时,唯一可用的方式似乎是:
self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj, self.user1 self.user2)
我想,这看起来并不坏,但它占用了三行,这完全没有必要。 必须有更好的方法,不是吗?
注:我知道有些人不喜欢80个字符,并创造了自己的极限。 我理解这个背后的动机并尊重它,但80个字符是我的首选限制。 请不要占用太多的空间,试图说服我去120或这样的一些这样的。
包装长行的首选方法是在括号,括号和大括号内使用Python的隐含行连续。 通过将expression式放在圆括号中,长行可以分成多行。 这些应该优先使用反斜杠来续行。 确保适当缩进续行。 打破二元运算符的首选位置是在运算符之后 ,而不是在它之前。
Python代码的PEP 8风格指南 (请参考示例中的链接)。
你的代码风格似乎坚持认为,如果你在一个括号内打破了一行,下面的代码需要与之alignment:
self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj, self.user1 self.user2)
如果您愿意放弃这个要求,您可以如下格式化代码,其中续行具有固定的双缩进:
self.SomeLongLongName = SomeLongLongName.SomeLongLongName( some_obj, self.user1, self.user2)
这样可以避免将代码写在页面的右边空白处,而且一旦习惯了,就会非常可读。 它还具有如下优点:如果修改“SomeLongLongName”的名称,则不必重新缩进以下所有行。 一个更长的例子如下:
if SomeLongLongName.SomeLongLongName( some_obj, self.user1, self.user2): foo() else: bar()
续行的双缩进允许您直观地将它们与缩进的行分开,因为它们位于if
或else
块中。
正如其他人已经指出,使用短名称也有帮助,但这并不总是可能的(如使用外部API时)。
self.SomeLongLongName = SomeLongLongName.\ SomeLongLongName(some_obj, self.user1, self.user2)
'\' 是你的朋友。 当然,你已经知道你可以用逗号分隔参数列表中的行,而不用'\'。 另外,如果你有很长的string:
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?"
变为:
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?"
这是有效的,因为Python将组合相邻的string文字,忽略相邻文字string之间的空格。
有些人引用了Rectangle类作为一个不好的例子。 pep8中的这个例子并不是唯一的方法。
原版的:
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)
这是我如何写它。
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)
原因是:
- 如果您的编辑器没有为您做这件事,而且由于有太多领先的白色空间IMO,难以阅读,所以需要额外的缩进来排列'('是浪费时间。
- 我试图尽可能晚地打破,除非在代码逻辑中有一个令人信服的理由。
- 用'('在这种情况下创build了与下一行完全相同的缩进级别… 非常糟糕的巧合 !双缩进延续线解决了这个问题。
- 如果不得不使用续行的原因是在一行上做太多的话,我更喜欢回避。 这里的例子是使用string格式运算符格式化的ValueError。 我设置味精来代替。 (注意:使用format方法格式化string是首选,从3.1开始,不推荐使用
%
)。
我第二迈克尔·肯特的答案(和我upvoted)。
而且,你应该读“PEP 8”并吸取教训。
http://www.python.org/dev/peps/pep-0008/
但是Python,它的命名空间,强大的function和面向对象的类,应该让你方便地使用短名称。
在C中,在许多情况下需要使用长标识符,因为名称在给定范围内必须是唯一的。 从而:
char *StringFromInt(int x); char *StringFromFloat(float x); char *StringFromUnsigned(unsigned int x); char *str_temp = strdup(StringFromUnsigned(foo_flags));
在Python中,所有这些都是内build的str()
:
temp = str(foo_flags)
在C ++中你有类和名称空间,所以你应该能够像Python一样使用面向对象的特性,但是在C中你需要全局唯一的名字,所以你经常需要这样做:
typedef struct s_foo { // struct members go here } FOO; FooAdd(); FooSubtract(); StringFromFoo();
在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)
为了自我logging的目的,您也可以使用非常长的variables名称。 我更喜欢简洁:
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())
我强烈喜欢第一,简洁的风格到第二。 根据PEP 8,我喜欢所有的小写variables名称(例如Circle
c
)。 在Python中,通常也推荐使用“Duck Typing”,就像我在简洁类中所做的那样:如果你希望radius是一个float,那么强制它在__init__()
一个float
而不是检查它的types。 同样,不是检查你是否通过了一个Point
实例,只是强迫你得到一个Point
。 如果参数作为一个Point
没有意义,您将让Point.__init__()
引发一个exception; Circle.__init__()
不需要额外的检查。 而且,你的Point.__init__()
函数可以明确地检查你是否传入了一个Point
实例,并且如果实例化了一个Point
实际上是非常昂贵的,那么就返回实例。 (在这个例子中,一个Point
实际上只是一对值,所以重新创build点可能足够快,而且你不需要检查。)
您可能会注意到我使用多行三重引号string的奇怪方式。 由于在Python中的缩进规则,我需要缩进三引号string,但我不想缩进string的行,因为缩进将是string的一部分。 真的,我想要所有的多条线都在左边缘,所以我可以清楚地看到这些线条得到了多长时间(并确保它们都是79个字符或更短)。 所以我使用反斜杠转义来允许多行string的第一行与其他行的左边距,而不在多行string的开头插入换行符。
无论如何,terser风格意味着你的variables名称,这样更容易打字,而且更容易使你的行符合PEP 8推荐的79列的限制。
使用一个字母长的内部成员名称,在一个像这样简单的类中,甚至不是完全可怕的。 只有两个成员,你可以很好地使用.c
为中心成员和.r
为半径。 但是,这并不能很好地扩展,而且.center
和.radius
仍然易于input,并且易于记忆。
提供信息文档也是一个非常好的主意。 您可以使用一些简洁的名称,但在文档string中有更长的解释。
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
命名空间很好。 注意当我们使用math.pi
时候有多清楚; 你知道这是math常量,你可以有一些局部variablespi
(也许是“Program Index”),它不会与math常量相冲突。
尝试缩短你的名字,如果你有这个选项。 否则,您可以使用\
字符继续行到下一行(以及其他类似的结构,如上面提到的)。
我发现自己使用了越来越多的中间variables,不仅有助于保持在80个字符之内,而且通过给出描述性的名字使代码更具可读性:
old_name = 'reallylonguglypath/to/current/file.foo' new_name = 'evenmoreuglylong/to/new/desination/for/file.foo' os.rename(old_name, new_name)
而不是:
os.rename("reallylonguglypath/to/current/file.foo", "evenmoreuglylong/to/new/desination/for/file.foo")
你可以用长模块和类名来做到这一点
method = SomeLongClassName.SomeLongMethodName self.SomeLongLongName = method(some_obj, self.user1, self.user2)