46

多年来,我编写的 Python 越多,我就越发现自己同意大多数准则,尽管出于自己的原因,我一直有意地破坏了一些准则。

我很想知道 PEP 8(或其他 PEP 也可能)中人们虔诚地坚持的内容以及原因,以及人们觉得不方便或不足的地方。

在我的情况下(以及在一般工作中),我们只偏离了少数几件事:

  • 下划线分隔的小写名称,我可以看到它的重点,因为它始终保持一致,但我们倾向于使用 lowerCamelCase,即使它偶尔会引入一些不一致(例如部分或错误大写的首字母缩写词和以下单词,它们是往往是一时兴起的电话)。主要是因为我们经常使用的几乎全部 API 都使用 camelCase(一些上,一些下),并且由于某种原因,我发现它更容易阅读,并且倾向于保留下划线作为分隔标记或规定的修饰/模糊。

  • 我仍然无法让自己按照 PEP 规定的对象内部的方式来分隔事物。new 和 init 我倾向于在类的正下方没有空行,因为我总是想在类名和 args 那里阅读它们,这些方法有助于类中相同的功能范围(比如 init、get 和 set相同的属性或属性集)我只分开一个空格,我喜欢类之间的三个空格,以及我不会在该对象的映射中精神聚合的方法之间的两个空格。同样,这纯粹是为了代码的视觉冲击力和可读性。我发现流控制中非常紧凑的内容以及方法和对象之间的这种间距始终将我的眼睛准确地引导到我希望它在代码被停放几个月后重新阅读的地方。

  • 相反,我坚持的一些事情让我在阅读其他方式时发疯,是制表符而不是空格(尤其是当我们使用的某些应用程序内编辑器实际上没有制表符替换功能时,这会极大地污染代码库原型阶段)。

  • 诸如导入之类的东西的顺序,以及什么导入,全局变量等。当这些文件混合或无序时,它真的让我对那些有大量导入的文件感到厌烦。

  • 语句中的空格,尤其是当人们使用制表符并尝试在 var 名称中使用不同长度的行对齐分配操作时(似乎没有办法说服那些这样做的人,看起来 excel 的代码并不整洁;)) .

  • 以及控制块内的间距,特别是当我在同一个流控制块内看到明显的随机间距,然后在对象内为方法使用类似数量的间距时。在我开始阅读该死的东西之前,我不得不编辑这些内容。

所以,这些都是我的,也是我“违反” PEP 背后的原因(有些是共享的,有些是同事不赞成的)。我很想知道其他 Pythonistas 在这些方面做什么和不做什么。

4

14 回答 14

58

“每行 79 个字符”部分是胡说八道。他们自己的示例显示了这样做时代码变得不可读:

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)

这就像试图
阅读这样写的
新闻文章。

十多年来,80 列终端一直没有成为一个严肃的开发环境。当我确实需要在紧要关头从残缺的 80x25 环境中进行编辑时,编辑器换行会带来一点不便;为了避免这种情况,我不会在正常开发期间损坏我的代码。

120 列换行对于现代开发来说是非常明智的,我对 140 没有任何问题。这个指南已经过时,遵循它会导致丑陋、难以阅读的代码。

于 2010-10-18T01:01:57.990 回答
57

PEP8 说要避免“在赋值(或其他)运算符周围使用多个空格以将其与另一个对齐”并且在数学运算符周围“永远不要使用多个空格”,但我不遵循这一点。

当相邻行相关或非常相似但不完全相同时,我经常添加“无关空格”:

search_start = (f - f_1/3) * n/fs
search_stop  = (f + f_1/3) * n/fs

 

b_lpf, a_lpf = filter(N, 2*pi*fc, 'low',  analog=True)
b_hpf, a_hpf = filter(N, 2*pi*fc, 'high', analog=True)

 

p[x >  1] =                         np.cosh(order * np.arccosh( x[x >  1]))
p[x < -1] = (1 - 2 * (order % 2)) * np.cosh(order * np.arccosh(-x[x < -1]))

 

b0 =  (1 + cos(w0))/2
b1 = -(1 + cos(w0))

同样,令人讨厌的是,我收到以可读方式格式化的数字数组的代码样式警告,它们通常由库本身格式化

a = array([[-0.198,  0.248, -1.17 , -0.629,  1.378],
           [-1.315,  0.947, -0.736, -1.388,  0.389],
           [ 0.241, -0.98 ,  0.535,  0.951,  1.143],
           [-0.601,  1.286, -0.947,  0.037, -0.864],
           [ 0.178, -0.289, -1.037, -1.453, -0.369]])

这会产生一堆 E201 E202 E222 违规。

显然,PEP8 宁愿将其格式化为这样,因为我们不能在逗号之前或括号之后有额外的空格,即使它提高了可读性:

a = array([[-0.198, 0.248, -1.17, -0.629, 1.378],
           [-1.315, 0.947, -0.736, -1.388, 0.389],
           [0.241, -0.98, 0.535, 0.951, 1.143],
           [-0.601, 1.286, -0.947, 0.037, -0.864],
           [0.178, -0.289, -1.037, -1.453, -0.369]])
于 2013-03-08T20:03:44.480 回答
25

PEP8

请注意,最重要的是,结束多行文档字符串的 """ 应该单独一行,并且最好在前面有一个空行,例如:

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.

"""

我觉得这很奇怪,因为它只是“无关的空白”,并且没有明显的原因将开场引文与闭幕引文区别对待。

PEP 257给出了一个基本原理:

BDFL 建议在多行文档字符串的最后一段和它的右引号之间插入一个空行,将右引号单独放在一行上。这样,Emacs 的 fill-paragraph 命令就可以用在上面了。

Emacs,真的吗?每个人都应该做一些奇怪的事情来迎合特定编辑工具中特定命令的特质?

我还认为将文档字符串的开头与引号放在同一行是很奇怪的(不是必需的,但推荐),同时坚持结束引号在自己的行上。我认为这更合乎逻辑,应该用于单行和多行文档字符串:

def foobang(bizbaz, plotz=None):
    """
    Return a foobang

    Optional plotz says to frobnicate the bizbaz first.
    """

    if plotz is not None:
        ...

更新:粗体部分已被删除,现在它只是说“将结束引号单独放在一行上”,并且“摘要行可能与开始引号在同一行或在下一行”。

于 2013-03-08T21:37:01.573 回答
20

我不同意这一点:

- Imports should usually be on separate lines, e.g.:

    Yes: import os
         import sys

    No:  import sys, os

我总是一起写简单的导入。我认为将它们全部写在单独的行上没有任何好处:它所做的只是在每个源文件的顶部添加膨胀,并将简洁且易于键入的内容转换为边界样板文件,例如。太冗长的东西开始很容易从其他文件中复制和粘贴。

这是立即可读和可以理解的:

import sys, os, time, gc, inspect, math, doctest

它很短,易于浏览,并且易于添加。import当然,如果一行中有太多语句,或者我需要from导入,我确实会使用多个语句。

我通常也会标准库导入与我自己的模块和其他库的导入分开,这与 PEP8 推荐的分组概念一致。

于 2010-10-18T01:29:05.110 回答
20

标准很关键,PEP 8 是我坚持的非常好的风格指南。我不同意的唯一准则是数学运算符周围的间距。例如 PEP8 坚持以下间距

Without PEP8                           With PEP8                
---------------------------------------------------------------- 
y = sqrt(x**2 + y**2)                  y = sqrt(x ** 2 + y ** 2) 
a*x**3 + b*x**2 + c*x + d              a * x ** 3 + b * x ** 2 + c * x + d 
10**(a*x + b)                          10 ** (a * x + b)  
F = V/(sqrt(g*h) + epsilon)            F = V / (sqrt(g * h) + epsilon) 
a*cos(nx/pi) + b*sin(nx/pi)            a * cos(nx / pi) + b * sin(nx / pi) 

我正在努力适应,但这是我正在努力的一个领域。有没有其他人也觉得 PEP8 间距使数学更难阅读?

更新:

PEP8更正以推荐左侧的格式,同时不鼓励右侧的格式:

是的:

i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

不:

i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
于 2011-03-06T14:26:18.497 回答
11

我公司的风格指南特别要求制表符,而不是空格。PEP 8 暗示空格更可取,但我们发现相反。我喜欢在 VIM 中看到代码缩进 4 个“空格”,一位同事更喜欢 Emacs 中的 8 个“空格”。使用选项卡让我们都可以设置我们的编辑器来显示我们喜欢的代码。

请注意,在其他基于 C 的语言中,缩进实际上只是格式化,但在 Python 中缩进是语法,因此我们认为indentation level 2应该由2某物(即制表符)而不是4某物8(即空格)来表示。

缩进字符选择可能是最初的神圣火焰战争(甚至在 VIM / Emacs 火焰战争之前),所以我确实希望被修改为被遗忘以表达对该主题的意见!

于 2014-04-23T09:13:17.993 回答
7

我的“承认违规”是关于“如果”

PEP8 在一行中没有多个语句,所以如果我们必须这样做:

if cond:
    actions
    ...

但是当只有一个动作时,我更喜欢一行,例如我更喜欢:

if a == 0: b = 0

比:

if a == 0:
     b = 0
于 2015-07-08T09:55:45.920 回答
5

多行条件等:PEP-8 明确表示要在二元运算符之后而不是在它之前中断。恐怕我看不到它的吸引力。在条件之前中断对我来说更有意义,因此在换行/续行中,每个子行都以条件开头:

if (condition1 \
        or condition2 \
        or condition3):
    do_something()

可以看出,我还想为子线添加额外的缩进,以便它们在视觉上与下面的块偏移。PEP-8 没有明确说明这一点(是吗?),但示例的子行与左括号对齐。

于 2010-10-18T01:12:23.643 回答
5

PEP 8 的“问题”在于它涉及个人偏好的领域,而这些领域对于大多数程序员来说都是非常情绪化的。

就我个人而言,camelCase 与下划线和列对齐指令是经常出现的问题。我也在这里看到了许多其他回复的意义,有时我故意破坏 PEP 8,因为在这种特殊情况下它只是“有意义”。

在我的 Python 编程生涯中,有一点我只是放弃并转向(使用)PEP 8。对于大多数项目来说,它相对容易,所以现在我唯一的主要问题是列对齐。那个太乱了,无法服从(尽管我无论如何都讨厌这样做)。无论如何,由于我的“放弃”,我的代码现在对我的同事来说更具可读性 - 而且 - 令人惊讶的是:甚至对我来说(除了列对齐 :p )。

我还必须认识到 PEP 8 对 python 本身所做的事情:在 2.x(不兼容)和 3.x(兼容)之间,我发现“总是知道”特定函数的名称要容易得多。Python 的“电池”现在分类得更好了。

于 2015-11-07T09:05:06.543 回答
4

PEP 8 说:

是的:

x = 1
y = 2
long_variable = 3

不:

x             = 1
y             = 2
long_variable = 3

我通常遵循它,但有时我会使用另一种变体,以提高可读性:

x =             1
y =             2
long_variable = 3
于 2010-10-18T01:21:57.760 回答
3

我总是使用 4 个空格,我尝试每行最多使用 79 个字符,有时这是不可能的。我过去也使用过诸如“import sys, os”之类的导入。一般来说,我尽量坚持 PEP 8。

编辑:也使用:

def foobar():
    """
    foobar
    """

用于文档

于 2010-10-18T00:48:33.543 回答
3

当我编写小脚本时,我经常只使用两个空格。

我总是对文档字符串使用相同的模式:

def function():
    """
    Even if it's a single line.
    """
于 2010-10-18T00:28:49.720 回答
1

python-mode.el,https: //launchpad.net/python-mode

同时允许自定义样式:

Mx 自定义变量 RET py-docstring-style RET

默认值为 pep-257-nn

实现的样式是 DJANGO、ONETWO、PEP-257、PEP-257-NN、SYMMETRIC 和 NIL。

NIL 值不关心引号位置,并将文档字符串视为普通字符串,任何其他值都可能导致以下文档字符串样式之一:

DJANGO:

"""
Process foo, return bar.
"""

"""
Process foo, return bar.

If processing fails throw ProcessingError.
"""

一二:

"""Process foo, return bar."""

"""
Process foo, return bar.

If processing fails throw ProcessingError.

"""

PEP-257:

"""Process foo, return bar."""

"""Process foo, return bar.

If processing fails throw ProcessingError.

"""

PEP-257-NN:

"""Process foo, return bar."""

"""Process foo, return bar.

If processing fails throw ProcessingError.
"""

对称:

"""Process foo, return bar."""

"""
Process foo, return bar.

If processing fails throw ProcessingError.
"""
于 2013-03-09T09:21:35.333 回答
0

我使用 pylint,http://pypi.python.org/pypi/pylint, 是一个极好的工具,可以让您和其他访问您的代码的开发人员保持代码的清洁和可读性。

不过,我并未涵盖您所说的所有主题,但它非常有用。

你可以有这样的报告:

carlos@debian:~/src/fcl/cltools$ pylint numbertoletters.py
No config file found, using default configuration
************* Module numbertoletters
C:  1: Missing docstring
C: 56:es_numero: Missing docstring
C: 56:es_numero: Invalid name "s" (should match [a-z_][a-z0-9_]{2,30}$)
C: 56:es_numero: Invalid name "s" (should match [a-z_][a-z0-9_]{2,30}$)
C: 69:convertnumbertoletters: Empty docstring
C: 90:convertnumbertoletters: Operator not preceded by a space
    numero='%(numero)09d' % {'numero' : int(parte_entera)}
          ^
C: 92:convertnumbertoletters: Comma not followed by a space
    for i in [0,3,6]:
               ^^

W: 69:convertnumbertoletters: Unused argument 'languaje'
C:108:unidades: Empty docstring
C:108:unidades: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)
C:108:unidades: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)
C:112:unidades: Invalid name "u" (should match [a-z_][a-z0-9_]{2,30}$)
C:118:teens: Empty docstring
C:118:teens: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)
C:118:teens: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)
C:122:teens: Invalid name "t" (should match [a-z_][a-z0-9_]{2,30}$)
C:127:tens: Empty docstring
C:127:tens: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)
C:127:tens: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)
C:131:tens: Invalid name "t" (should match [a-z_][a-z0-9_]{2,30}$)
C:137:tercia: Empty docstring
C:141:tercia: Operator not preceded by a space
    numero='%(numero)03d' % {'numero' : int(num)}
          ^
C:143:tercia: Invalid name "a" (should match [a-z_][a-z0-9_]{2,30}$)
C:144:tercia: Invalid name "b" (should match [a-z_][a-z0-9_]{2,30}$)
C:145:tercia: Invalid name "c" (should match [a-z_][a-z0-9_]{2,30}$)
C:163:tercia: Operator not followed by a space
                resultado ='veinti '+unidades(c)
                          ^
C:165:tercia: Operator not followed by a space
        elif b >=3 and b <= 9:
               ^^
C:178:tercia: Operator not followed by a space
                resultado ='ciento '+unidades(c)
                          ^
C:192:tercia: Operator not followed by a space
                resultado ='ciento veinti '+unidades(c)
                          ^
C:204:tercia: Operator not preceded by a space
            prefix='quinientos '
                  ^
C:206:tercia: Operator not preceded by a space
            prefix='setecientos '
                  ^
C:208:tercia: Operator not preceded by a space
            prefix='novecientos '
                  ^
C:210:tercia: Operator not preceded by a space
            prefix=unidades(a)+'cientos '
                  ^
R:137:tercia: Too many return statements (23/6)
R:137:tercia: Too many branches (41/12)
R:137:tercia: Too many statements (73/50)


Report
======
141 statements analysed.

Raw metrics
-----------

+----------+-------+------+---------+-----------+
|type      |number |%     |previous |difference |
+==========+=======+======+=========+===========+
|code      |144    |68.25 |NC       |NC         |
+----------+-------+------+---------+-----------+
|docstring |5      |2.37  |NC       |NC         |
+----------+-------+------+---------+-----------+
|comment   |57     |27.01 |NC       |NC         |
+----------+-------+------+---------+-----------+
|empty     |5      |2.37  |NC       |NC         |
+----------+-------+------+---------+-----------+



Statistics by type
------------------

+---------+-------+-----------+-----------+------------+---------+
|type     |number |old number |difference |%documented |%badname |
+=========+=======+===========+===========+============+=========+
|module   |1      |NC         |NC         |0.00        |0.00     |
+---------+-------+-----------+-----------+------------+---------+
|class    |0      |NC         |NC         |0           |0        |
+---------+-------+-----------+-----------+------------+---------+
|method   |0      |NC         |NC         |0           |0        |
+---------+-------+-----------+-----------+------------+---------+
|function |6      |NC         |NC         |0.00        |0.00     |
+---------+-------+-----------+-----------+------------+---------+



Duplication
-----------

+-------------------------+------+---------+-----------+
|                         |now   |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines      |0     |NC       |NC         |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |NC       |NC         |
+-------------------------+------+---------+-----------+



Messages by category
--------------------

+-----------+-------+---------+-----------+
|type       |number |previous |difference |
+===========+=======+=========+===========+
|convention |32     |NC       |NC         |
+-----------+-------+---------+-----------+
|refactor   |3      |NC       |NC         |
+-----------+-------+---------+-----------+
|warning    |1      |NC       |NC         |
+-----------+-------+---------+-----------+
|error      |0      |NC       |NC         |
+-----------+-------+---------+-----------+



Messages
--------

+-----------+------------+
|message id |occurrences |
+===========+============+
|C0103      |14          |
+-----------+------------+
|C0322      |6           |
+-----------+------------+
|C0112      |5           |
+-----------+------------+
|C0323      |4           |
+-----------+------------+
|C0111      |2           |
+-----------+------------+
|W0613      |1           |
+-----------+------------+
|R0915      |1           |
+-----------+------------+
|R0912      |1           |
+-----------+------------+
|R0911      |1           |
+-----------+------------+
|C0324      |1           |
+-----------+------------+



Global evaluation
-----------------
Your code has been rated at 7.45/10

I hope it helps.

我强烈建议使用 pylint 来评估您的代码,并保持标准的编程方式,特别是在开发人员社区中。=)

我希望它有所帮助。

于 2010-10-18T00:55:09.117 回答