What PEP 8 guidelines do you ignore, and which one

2019-03-10 15:56发布

问题:

Over the years, the more Python I write, the more I find myself agreeing with most of the guidelines, though I consistently and intentionally break some for my own reasons.

I'd be curious to know what in PEP 8 (or other PEPs too maybe) people religiously stick to and why, and what people find inconvenient or inadequate.

In my case (and at work in general), there's only a handful of things we deviate from:

  • Underscore separated lowercase names, I can see the point of it, as it will unfailingly be consistent, but we tend to use lowerCamelCase, even if it will occasionally introduce some inconsistencies (such as partially or mis-capitalized acronyms and following words, which are often down to spur-of-the-moment calls). Mostly because the near totality of the APIs we routinely use use camelCase (some upper, some lower), and because for some reason I find it easier to read, and tend to reserve underscores as separation tokens or prescribed mangling/obscuring.

  • I still can't get myself to space things out the way the PEP prescribes inside objects. new and init I tend to leave right under the class with no blank lines as I always want to read them right there with the class name and args, methods that contribute to the same scope of functionality in the class (say init, get and set of the same attrib or set of attribs) I only single-space apart, and I like three spaces between classes, and two between methods I wouldn't mentally aggregate in the map of that object. This is, again, purely for the visual impact and readability of the code. I find that very compact contents inside flow control and this kind of spacing between methods and objects consistently leads my eye exactly where I want it to go on re-readings months after the code had been parked. It also responds well to folding in my editors of choice.

  • Some things instead I stick to, that drive me nuts when I read otherwise written, is tabs instead of spaces (especially when some in-app editors we use don't really have tab replacement functionalities, contributing considerably to pollution in the code base at prototyping stage).

  • Order of things such as imports, and what imports, globals etc. It really throws me off on files that have large amounts of imports when those are mixed up or out of order.

  • Whitespaces in statements, especially when people use tabs AND try to align assignment ops across lines with different length in var names (and there seems to be no way to persuade those who do it that an excel looking piece of code is NOT neater ;) ).

  • And spacing within a control block, particularly when I see apparently random spacing within the same flow control block, and then similar amounts of spacing used within the object for methods. I'm compelled to edit those before I can even start reading the damn thing.

So, those are mine, and the reasoning behind my "violations" of the PEP (some shared, some frowned upon by colleagues). I'd be very curious to read what other Pythonistas do and don't do in those regards.

回答1:

The "79 characters per line" part is nonsense. Their very own example shows how unreadable code becomes when doing this:

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)

It's like try-
ing to read
a news arti-
cle written
like this.

80-column terminals havn't been a serious development environment for over a decade. When I do need to edit from a crippled 80x25 environment in a pinch, editor wrapping is a minor inconvenience; I'm not going to maim my code during normal development just to avoid that.

120 column wrapping is perfectly sensible for modern development, and I have no problem with 140. This guideline is obsolete and following it results in ugly, hard-to-read code.



回答2:

PEP8 says to avoid "More than one space around an assignment (or other) operator to align it with another" and "never use more than one space" around math operators, but I don't follow this.

I often add "extraneous whitespace" when neighboring lines are related or very similar, but not quite the same:

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))

Similarly, it's annoying that I get code style warnings for arrays of numbers formatted in the readable way that they are normally formatted by the library itself:

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]])

PEP8 would rather have it formatted it like this, apparently, because we can't have extra whitespace before commas or after brackets:

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]])


回答3:

PEP8 says

Note that most importantly, the """ that ends a multiline docstring should be on a line by itself, and preferably preceded by a blank line, e.g.:

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.

"""

I find this rather bizarre, since it's just "extraneous whitespace" and treats opening quotations differently from closing quotations for no obvious reason.

A rationale is given is in PEP 257:

The BDFL recommends inserting a blank line between the last paragraph in a multi-line docstring and its closing quotes, placing the closing quotes on a line by themselves. This way, Emacs' fill-paragraph command can be used on it.

Emacs, really? Everyone should do weird things to cater to the idiosyncrasies of a particular command in a particular editing tool?

I also think it's weird to put the beginning of the docstring on the same line as the quotes (not required, but recommended), while insisting that the closing quotations be on their own line. I think this is more logical, and should be used for both single line and multi-line docstrings:

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

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

    if plotz is not None:
        ...

Update: The bold part has been removed and now it just says to "place the closing quotes on a line by themselves", and that the "summary line may be on the same line as the opening quotes or on the next line".



回答4:

I don't agree with this:

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

    Yes: import os
         import sys

    No:  import sys, os

I always write simple imports together. I don't see any advantage to writing them all on separate lines: all it does is add bloat to the top of each source file, and turn something concise and easy to type into something that's borderline boilerplate, eg. something that's so verbose it starts to be tempting to copy and paste from other files.

This is instantly readable and understandable:

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

It's short, easy to skim, and easy to add to. I do use multiple import statements if there are too many on one line, of course, or if I need a from import.

I also do generally keep standard library imports separate from imports of my own modules and other libraries, which agrees with the concept of grouping PEP8 recommends.



回答5:

Standards are critical and PEP 8 is a very good style guide that I insist on. The only guideline I disagree with is the spacing around mathematical operators. For example PEP8 insists on the following spacings

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) 

I am trying to conform, but this is the one area where I am struggling. Do anyone else also feel that PEP8 spacing makes mathematics harder to read?

Update:

PEP8 was corrected to recommend the formatting on the left while discouraging the formatting on the right:

Yes:

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

No:

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


回答6:

My company's style guidelines specifically call for tabs, not spaces. PEP 8 hints that spaces are preferable but we've found the opposite. I like to see code indented 4 'spaces' in VIM, a coworker prefers 8 'spaces' in Emacs. Using tabs lets us both set our editors to show the code as we prefer it.

Note that in other C-based languages, indentation is in fact merely formatting but in Python indentation is syntax, thus we feel that indentation level 2 should be represented by 2 of something (i.e. tabs) not 4 or 8 of something (i.e. spaces).

Indentation character selection is probably the original holy flame war (even before the VIM / Emacs flamewars) so I do expected to be modded to oblivion for expressing an opinion on the subject!



回答7:

Multi-line conditionals etc.: PEP-8 explicitly says to break after a binary operator instead of before it. I'm afraid that I cannot see the appeal of that. It makes a lot more sense to me to break before the conditional, so that in wrapped/continued lines, each sub-line begins with the conditional:

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

As can be seen, I also like to add an extra indent for the sublines, so that they are visually offset from the block below. PEP-8 does not say anything explicit about this (does it?), but the examples have the sub-lines lining up with the opening parentheses.



回答8:

I always use 4 spaces, I try to use maximum of 79 chars per line, not possible sometime. I also used imports like "import sys, os" in the past. In general I try to stick to PEP 8.

Edit: also use:

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

for documentation



回答9:

PEP 8 says:

Yes:

x = 1
y = 2
long_variable = 3

No:

x             = 1
y             = 2
long_variable = 3

i usually follow it, but i sometimes use another variant, for the sake of better readability:

x =             1
y =             2
long_variable = 3


回答10:

My "admit violation" is about "if"

PEP8 says no multiple statement in one line, so for if we must do this :

if cond:
    actions
    ...

But when there only one action, I prefer all in one line, e.g. I prefer :

if a == 0: b = 0

than:

if a == 0:
     b = 0


回答11:

The "problem" with PEP 8 is that it treads on areas of personal preference that are subject to quite high amounts of emotion for most programmers.

For me personally, camelCase vs underscores and column alignment directives were constant issues. I also see point in lots of other responses here and sometimes I break PEP 8 intentionally because in that particular case it just "makes sense".

There was a point in my Python programming career when I simply gave up and turned to (using) PEP 8. It was relatively easy for most items, so nowadays the only major issue I still have is the column alignment. That one's just too messy to obey(though I hatefully do anyway). Anyway, as a result of my "giving up", my code is now much more readable to my colleagues - and - surprisingly: even to me (except for the column alignment thingy :p ).

I must also recognize what PEP 8 has done for python itself: between 2.x (non-compliant) and 3.x (compliant), I find it much easier to "always know" what a particular function's name will be. Python's "batteries" are sorted much nicer now.



回答12:

I use pylint, http://pypi.python.org/pypi/pylint, it is an excellent tool to try to keep your code clean and readable for you and other developers accessing your code.

I does not cover all topics you said, though, but it is very useful.

You can have reports like this one:

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.

I highly recommend the use of pylint to rate your code, and keep a standard way of programming, specially in a community of developers. =)

I hope it helps.



回答13:

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

meanwhile allows customization of style:

M-x customize-variable RET py-docstring-style RET

Default value is pep-257-nn

Implemented styles are DJANGO, ONETWO, PEP-257, PEP-257-NN, SYMMETRIC, and NIL.

A value of NIL won't care about quotes position and will treat docstrings a normal string, any other value may result in one of the following docstring styles:

DJANGO:

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

"""
Process foo, return bar.

If processing fails throw ProcessingError.
"""

ONETWO:

"""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.
"""

SYMMETRIC:

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

"""
Process foo, return bar.

If processing fails throw ProcessingError.
"""


回答14:

When I'm writing tiny scripts I often just use two spaces.

I always use the same pattern for docstrings:

def function():
    """
    Even if it's a single line.
    """


标签: python pep8 pep