一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

一份大廠都在用的 Python 編碼規(guī)范

 龍書意 2022-11-01 發(fā)布于廣東

前言:為了讓不同編碼習(xí)慣的開發(fā)者更好的協(xié)作配合,并且形成良好的基礎(chǔ)編碼規(guī)范與風(fēng)格,我們以 PEP8 為基礎(chǔ),修改了陳舊的規(guī)則,豐富了示例,并整理了工作中常見的不規(guī)范操作,最終形成此 Python 編碼規(guī)范與風(fēng)格。

  • 一、編碼風(fēng)格

    • 1.1 縮進

    • 1.2 每行最大長度

    • 1.3 空白符

    • 1.4 操作符

    • 1.5 括號

    • 1.6 空行

    • 1.7 源文件編碼

    • 1.8 Shebang

    • 1.9 模塊引用 (import)

    • 1.10 模塊中的魔術(shù)變量 (dunders)

    • 1.11 注釋

    • 1.12 文檔字符串

    • 1.13 類型提示

    • 1.14 字符串

    • 1.15 文件和 sockets

    • 1.16 訪問控制

    • 1.17 Main

    • 1.18 命名

  • 二、編碼規(guī)范

    • 2.1 三目運算符

    • 2.2 None 條件的判斷

    • 2.3 lambda 匿名函數(shù)

    • 2.4 異常

    • 2.5 條件表達式

    • 2.6 True/False 布爾運算

    • 2.7 列表推導(dǎo)式

    • 2.8 函數(shù)

    • 2.9 變量

  • 工具與配置

    • flake8

    • pylint

    • black

    • EditorConfig

注意事項

本規(guī)范適用于所有使用 Python 語言作為開發(fā)語言的軟件產(chǎn)品。

由于 Python2 在 2020 年停止維護,建議新增的項目使用 Python3.6+,可以使用到更多的高級特性。如果項目有兼容性需求需要支持老版本 Python 的,那么不涉及的特性可以忽略。本規(guī)范的示例采用符合 Python3.6+ 的語法。

  • 必須(Mandatory):用戶必須采用;
  • 推薦(Preferable):用戶理應(yīng)采用,但如有特殊情況,可以不采用;
  • 可選(Optional):用戶可參考,自行決定是否采用;

未明確指明的則默認(rèn)為 必須(Mandatory)。

一、編碼風(fēng)格

規(guī)范地代碼布局有助于幫助開發(fā)者更容易地理解業(yè)務(wù)邏輯。

1.1 縮進

1.1.1 【必須】 對于每級縮進,統(tǒng)一要求使用 4 個空格,而非tab鍵。pylint:bad-indentation.

1.1.2 【必須】 續(xù)行,要求使用括號等定限界符,并且需要垂直對齊。

正確示范

# 與定界(括號)符對齊
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# 換行并增加4個額外的空格(一級縮進)
def long_function_name(
        var_one, var_two, var_three,
        var_four)
:

    print(var_one)

# 懸掛需要增加一級縮進
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

錯誤示范


# 當(dāng)不使用垂直對齊時,第一行不允許加參數(shù)
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# 下面這種情況,需要增加額外的縮進,否則無法區(qū)分代碼所在的縮進級別
def long_function_name(
    var_one, var_two, var_three,
    var_four)
:

    print(var_one)

1.1.3 【推薦】 如果包含定界符(括號,中括號,大括號)的表達式跨越多行,那么定界符的擴回符, 可以放置與最后一行的非空字符對齊或者與構(gòu)造多行的開始第一個字符對齊。

正確示范

# 與最后一行的非空字符對齊
my_list = [
    123,
    456,
    ]
result = some_function_that_takes_arguments(
    'a''b''c',
    'd''e''f',
    )

# 或者與開始構(gòu)造多行的第一個字符對齊
my_list = [
    123,
    456,
]
result = some_function_that_takes_arguments(
    'a''b''c',
    'd''e''f',
)

1.1.4 【推薦】 對于會經(jīng)常改動的函數(shù)參數(shù)、列表、字典定義,建議每行一個元素,并且每行增加一個 ,

正確示范

yes = ('y''Y''yes''TRUE''True''true''On''on''1')  # 基本不再改變

kwlist = [
    'False',
    'None',
    'True',
    'and',
    'as',
    'assert',
    ...
    'yield',  # 最后一個元素也增加一個逗號 ,方便以后diff不顯示此行
]

person = {
    'name''bob',
    'age'12,      # 可能經(jīng)常增加字段
}

錯誤示范

# 關(guān)鍵字會不斷增加,每個元素都換行
kwlist = ['False''None''True''and''as',
          'assert''async', ...
]

person = {'name''bob''age'12}

1.1.5 【可選】 對于 if 判斷,一般來說盡量不要放置過多的判斷條件。換行時增加 4 個額外的空格。pycodestyle:E129 visually indented line with same indent as next logical line.

備注:PEP8 沒有明確規(guī)定,以下幾種都是允許的。建議使用前面 2 種方法,后 2 種會與已有的開源工具沖突。

正確示范

# 更推薦:在續(xù)行中,增加額外的縮進級別。允許 and 操作符在前
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

# 更推薦:在續(xù)行中,增加額外的縮進級別
if (this_is_one_thing and
        that_is_another_thing):
    do_something()

# 允許:與定界符(括號)對齊,不需要額外的縮進
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 允許:增加注釋,編輯器會提示語法高亮,有助于區(qū)分
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

1.2 每行最大長度

1.2.1 【必須】 每行最多不超過 120 個字符。每行代碼最大長度限制的根本原因是過長的行會導(dǎo)致閱讀障礙,使得縮進失效。pylint:line-too-long.

除了以下兩種情況例外:

  1. 導(dǎo)入模塊語句。
  2. 注釋中包含的 URL。

如果需要一個長的字符串,可以用括號實現(xiàn)隱形連接。

正確示范

x = ('This will build a very long long '
     'long long long long long long string')

1.3 空白符

1.3.1 【必須】 在表達式的賦值符號、操作符左右至少有一個空格。

正確示范

x = y + 1

錯誤示范

x=y+1
x = y+1

1.3.2 【必須】 禁止行尾空白。pylint:trailing-whitespace.

行尾空白雖然不會造成功能性異常,但是這些空白字符會被源碼管理系統(tǒng)標(biāo)記出來顯示為差異,對開發(fā)人員造成困惱。

正確示范

# YES: 尾部沒有空白符號
+ para = {}
+ para = {}  # comment

錯誤示范

# No: 結(jié)尾存在多余空白符號
- para = {}·····
- para = {}  # comment······

1.4 操作符

1.4.1 【推薦】 Python 沒有三目操作符,對于二目操作符來說,操作符允許在換行符之后出現(xiàn)。

備注:pycodestyle 工具與此條目相反,PEP8 推薦操作符在這之前,更具備可讀性。PEP8: Should a Line Break Before or After a Binary Operator?。屏蔽 pycodestyle:W503 line break before binary operator

正確示范

# YES: 易于將運算符與操作數(shù)匹配,可讀性高
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

錯誤示范

# No: 運算符的位置遠離其操作數(shù)
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

1.5 括號

1.5.1 【必須】 tuple 元組不允許逗號結(jié)尾,顯式增加括號規(guī)避。即使一個元素也加上括號。pylint:trailing-comma-tuple.

行尾的逗號可能導(dǎo)致本來要定義一個簡單變量,結(jié)果變成 tuple 變量。

正確示范

trailingcomma = (['f'],)

return (1,)

錯誤示范

trailingcomma = ['f'],   # tuple

return 1,

1.6 空行

1.6.1 【必須】 模塊中的一級函數(shù)和類定義之間,需要空兩行。pycodestyle:E302 expected 2 blank lines.

1.6.2 【必須】 類中函數(shù)定義之間,空一行。pycodestyle:E302 expected 1 blank line.

1.6.3 【必須】 源文件須使用且僅使用 一個換行符 作為結(jié)尾。pylint:missing-final-newline, trailing-newlines.

1.6.4 【必須】 通常每個語句應(yīng)該獨占一行。pylint:multiple-statements.

如果測試結(jié)果與測試語句在一行放得下,你也可以將它們放在同一行。如果是 if 語句,只有在沒有 else 時才能這樣做。特別地,絕不要對 try/except 這樣做,因為 tryexcept 不能放在同一行。

正確示范

if foo:
    bar(foo)
else:
    baz(foo)

try:
    bar(foo)
except ValueError:
    baz(foo)

錯誤示范

if foo: bar(foo)
else:   baz(foo)

try:               bar(foo)
except ValueError: baz(foo)

try:
  bar(foo)
except ValueError: baz(foo)

1.6.5 【推薦】 可以在代碼段中的空一行來區(qū)分不同業(yè)務(wù)邏輯塊。

'''This is the example module.

This module does stuff.
'''



import os


def foo():
    pass


class MyClass():

    def __init__(self):
        pass

    def foo(self):
        pass


class AnotherClass(object):
    '''Another class.

    This is some comments for another class
    '''


    def __init__(self,
                 a,
                 b)
:

        if a > b:
            self._min = b
            self._max = a

        else:
            self._min = a
            self._max = b

        self._gap = self._max = self._min

    def foo(self):
        pass

1.7 源文件編碼

1.7.1 【必須】 源文件編碼需統(tǒng)一使用 UTF-8 編碼,以下內(nèi)容需要增加到每一個 python 文件的頭部。

# -*- coding: utf-8 -*-

1.7.2 【必須】 ** 避免不同操作系統(tǒng)對文件換行處理的方式不同,一律使用 LF**。pylint:mixed-line-endings, unexpected-line-ending-format.

1.8 Shebang

1.8.1 【必須】 程序的 main 文件應(yīng)該以 #!/usr/bin/env python2 或者 #!/usr/bin/env python3 開始, 可以同時支持 Python2、Python3 的#!/usr/bin/env python

非程序入口的文件不應(yīng)該出現(xiàn) Shebang 。

正確示范

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#

1.9 模塊引用 (import)

1.9.1 【必須】 每個導(dǎo)入應(yīng)該獨占一行。pylint:multiple-imports.

正確示范

import os
import sys

錯誤示范

import os, sys

1.9.2 【必須】 導(dǎo)入總應(yīng)該放在文件頂部,位于模塊注釋和文檔字符串之后,模塊全局變量和常量之前。pylint:wrong-import-order.

導(dǎo)入應(yīng)該按照從最通用到最不通用的順序分組,每個分組之間,需要空一行:

  • 標(biāo)準(zhǔn)庫導(dǎo)入
  • 第三方庫導(dǎo)入
  • 本地導(dǎo)入

每種分組中,建議每個模塊的完整包路徑按 字典序 排序,并忽略大小寫。

正確示范

import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar

1.9.3 【必須】 避免使用 from <module> import *,因為可能會造成命名空間的污染。pylint:wildcard-import.

1.9.4 【必須】 禁止導(dǎo)入了模塊卻不使用它。pylint:unused-import.

正確示范

import os  # used

dir_path = os.path.abspath('.')

錯誤示范

import os   # unused !

# dir_path = os.path.abspath('.')

1.10 模塊中的魔術(shù)變量 (dunders)

1.10.1 【必須】 對于兩個 _  開頭和兩個 _ 結(jié)尾的變量, 如 __all__,__author__,__version__等,應(yīng)該放在模塊文檔之后, 其他模塊導(dǎo)入之前(__future__ 除外)。

1.10.2 【必須】 Python 要求 future 導(dǎo)入必須出現(xiàn)在其他模塊導(dǎo)入之前。pylint:misplaced-future.

正確示范

# -*- coding: utf-8 -*-
#
# Copyright @ 2020 Tencent.com

'''This is the example module.

This module does stuff.
'''


from __future__ import barry_as_FLUFL

__all__ = ['a''b''c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'

import os
import sys

1.11 注釋

有效的注釋有助于幫助開發(fā)者更快地理解代碼,模塊,函數(shù),方法,以及行內(nèi)注釋的都有各自的風(fēng)格。

1.11.1 【必須】 所有#開頭的注釋,必須與所在的代碼塊同級,并置放在代碼之上。pycodestyle:E262 inline comment should start with '# '.

1.11.2 【必須】 注釋的每一行都應(yīng)該以#和一個空格開頭。pycodestyle:E266 too many leading '#' for block comment, E262 inline comment should start with '# '.

1.11.3 【必須】 行內(nèi)注釋#與代碼離開至少 2 個空格。pycodestyle:E261 at least two spaces before inline comment.

1.11.4 【必須】 塊注釋:對于復(fù)雜的操作,可以在代碼之前寫若干行注釋,對簡單的代碼,可以放在行內(nèi)。與代碼離開至少 2 個空格。

正確示范

# this is a very complex operation, please
# read this carefully

if i & (i-1) == 0:
    # do my job ...

# 單行注釋,為可讀性,至少離開代碼2個空格
x = x + 1                 # Compensate for border

1.11.5 【必須】 TODO 注釋需要加上名字。

TODO 注釋應(yīng)該在所有開頭處包含 TODO 字符串,緊跟著是用括號括起來的你的名字, email 地址或其它標(biāo)識符,然后是一個可選的冒號。接著必須有一行注釋,解釋要做什么。主要目的是為了有一個統(tǒng)一的 TODO 格式,這樣添加注釋的人就可以搜索到 (并可以按需提供更多細節(jié))。寫了 TODO 注釋并不保證寫的人會親自解決問題。當(dāng)你寫了一個 TODO,請注上你的名字。

為臨時代碼使用 TODO 注釋,它是一種短期解決方案。常見的 IDE 在提交代碼時, 會檢查變更中包含了 TODO 并提醒開發(fā)者,防止提交是忘記還有未完成的代碼。如果 TODO 是將來做某事的形式,那么請確保包含一個指定的日期或者一個特定的事件(條件)。相同地,也可以留下 FIXME, NOTES 注釋。

正確示范

# TODO(zhangsan): Change this to use relations.
# FIXME(zhangsan@xx.com): Please fix me here.
# NOTES(zhangsan): This is some notes.

1.12 文檔字符串

Docstring 文檔字符串提供了將文檔與 Python 模塊,函數(shù),類和方法相關(guān)聯(lián)的便捷方法。

def foobar():
    '''Return a foobang

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

PEP 257 全面描述了文檔字符串的風(fēng)格。

1.12.1 【推薦】 需對外發(fā)布的 public 模塊,函數(shù),類,方法等需要包含文檔字符串。內(nèi)部使用的方法,函數(shù)等,要求使用簡單的注釋描述功能。pylint:missing-module-docstring, missing-class-docstring, missing-function-docstring.

一個函數(shù)或方法,如果可以直接被其他開發(fā)者使用,需要提供文檔明確其含義,需要指出輸入,輸出,以及異常內(nèi)容。

1.12.2 【必須】 第一行應(yīng)為文檔名,空一行后,輸入文檔描述。

1.12.3 【推薦】 在使用文檔字符串時,推薦使用 reStructuredText 風(fēng)格類型。

正確示范

def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
    '''Fetches rows from a Bigtable.

    Retrieves rows pertaining to the given keys from the Table instance
    represented by big_table.  Silly things may happen if
    other_silly_variable is not None.

    :param big_table: An open Bigtable Table instance.
    :param keys: A sequence of strings representing the key of each table row
        to fetch.
    :param other_silly_variable: Another optional variable, that has a much
        longer name than the other args, and which does nothing.

    :return: A dict mapping keys to the corresponding table row data
        fetched. Each row is represented as a tuple of strings. For
        example:

        {'Serak': ('Rigel VII', 'Preparer'),
         'Zim': ('Irk', 'Invader'),
         'Lrrr': ('Omicron Persei 8', 'Emperor')}

        If a key from the keys argument is missing from the dictionary,
        then that row was not found in the table.

    :raises ValueError: if `keys` is empty.
    :raises IOError: An error occurred accessing the bigtable.Table object.
    '''

    pass

1.12.4 【推薦】 類應(yīng)該在其定義下有一個用于描述該類的文檔字符串。如果類有公共屬性 (Attributes),那么文檔中應(yīng)該有一個屬性 (Attributes) 段, 并且應(yīng)該遵守和函數(shù)參數(shù)相同的格式。

正確示范

class SampleClass(object):
    '''Summary of class here.

    Longer class information....
    Longer class information....

    :ivar likes_spam: A boolean indicating if we like SPAM or not.
    :ivar eggs: An integer count of the eggs we have laid.
    '''


    def __init__(self, likes_spam=False):
        '''Inits SampleClass with blah.'''
        self.likes_spam = likes_spam
        self.eggs = 0

    def public_method(self):
        '''Performs operation blah.'''

1.13 類型提示

Python 是動態(tài)語言,在運行時無需指定變量類型。雖然運行時不會執(zhí)行函數(shù)與變量類型注解,但類型提示有助于閱讀代碼、重構(gòu)、靜態(tài)代碼檢查與 IDE 的語法提示。推薦在項目中使用該特性。更多使用可以參考 類型標(biāo)注支持。

類型提示示例代碼

from typing import List


class Container(object):
    def __init__(self) -> None:
        self.elements: List[int] = []

    def append(self, element: int) -> None:
        self.elements.append(element)


def greeting(name: str) -> str:
    return 'Hello ' + name


# 變量定義
lang: str = 'zh'
success_code: int = 0

1.13.1 【必須】 模塊級變量,類和實例變量以及局部變量的注釋應(yīng)在冒號后面有一個空格。pycodestyle:E231 missing whitespace after ':'.

1.13.2 【必須】 冒號前不應(yīng)有空格。

1.13.3 【必須】 如果有賦值符,則等號在兩邊應(yīng)恰好有一個空格。pycodestyle:E225 missing whitespace around operator.

正確示范

code: int = 10

class Point(object):
    coords: Tuple[int, int] = (00)
    label: str = '<unknown>'

def broadcast(servers: Sequence[Server],
              message: str = 'spaces around equality sign')
 -> None:

    pass

錯誤示范

code:int  # No space after colon
code : int  # Space before colon

class Test(object):
    result: int=0  # No spaces around equality sign

1.13.4 【推薦】 當(dāng)使用類型提示出現(xiàn)循環(huán)引用時,可以在導(dǎo)入的頭部使用 if typing.TYPE_CHECKING , 且對類型注解使用雙引號單引號進行修飾。

正確示范

import typing

if typing.TYPE_CHECKING:  # 運行時不導(dǎo)入
    # For type annotation
    from typing import Any, Dict, List, Sequence  # NOQA
    from sphinx.application import Sphinx  # NOQA


class Parser(docutils.parsers.Parser):

    def set_application(self, app: 'Sphinx') -> None:  # 同時采用引號
        pass

1.14 字符串

1.14.1 【推薦】 即使參數(shù)都是字符串,也要使用 % 操作符或者格式化方法格式化字符串。不過也不能一概而論,你需要在 + 和 % 之間權(quán)衡。

正確示范

# 更推薦
x = f'name: {name}; score: {n}'  # Python3.6+ 以上支持
x = 'name: {name}; score: {n}'.format(name=name, n=n)
x = 'name: {name}; score: {n}'.format(**{'name': name, 'n': n})
x = 'name: %(name)s; score: %(n)d' % {'name': name, 'n': n}

# 可接受
x = '%s, %s!' % (imperative, expletive)
x = '{}, {}!'.format(imperative, expletive)
x = 'name: %s; score: %d' % (name, n)
x = 'name: {}; score: {}'.format(name, n)

錯誤示范

x = '%s%s' % (a, b)  # use + in this case
x = '{}{}'.format(a, b)  # use + in this case
x = imperative + ', ' + expletive + '!'
x = 'name: ' + name + '; score: ' + str(n)

1.14.2 【推薦】 避免在循環(huán)中用 ++= 操作符來累加字符串。由于字符串是不可變的,這樣做會創(chuàng)建不必要的臨時對象,并且導(dǎo)致二次方而不是線性的運行時間。作為替代方案,你可以將每個子串加入列表,然后在循環(huán)結(jié)束后用 .join 連接列表。(也可以將每個子串寫入一個 io.StringIO 緩存中。) pylint:consider-using-join.

正確示范

items = ['<table>']
for last_name, first_name in employee_list:
    items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
items.append('</table>')
employee_table = ''.join(items)

錯誤示范

employee_table = '<table>'
for last_name, first_name in employee_list:
    employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
employee_table += '</table>'

1.14.3 【推薦】 在同一個文件中,保持使用字符串引號的一致性。使用單引號' 或者雙引號 ' 引用字符串,并在同一文件中一直沿用這種風(fēng)格。當(dāng)字符串中包含單引號或者雙引號時,為提高可讀性,使用另外一種引號,代替轉(zhuǎn)義字符。

正確示范

Python('Why are you hiding your eyes?')
Gollum('I'm scared of lint errors.')
Narrator(''Good!' thought a happy Python reviewer.')

錯誤示范

Python('Why are you hiding your eyes?')
Gollum('The lint. It burns. It burns us.')
Gollum('Always the great lint. Watching. Watching.')

1.14.4 【必須】 如果要引用的字符串為多行時,需要使用雙引號引用字符串。

1.14.5 【必須】 文檔字符串(docstring)必須使用三重雙引號 '''。

1.14.6 【可選】 避免在代碼中使用三重引號 ''',因為當(dāng)使用三重引號時,縮進方式與其他部分不一致,容易引起誤導(dǎo)。

正確示范

print('This is much nicer.\n'
      'Do it this way.\n')

錯誤示范

print('''This is pretty ugly.
Don't do this.
'''
)

1.14.7 【推薦】 檢查前綴和后綴時,使用 .startswith().endswith() 代替字符串切片。

正確示范

if foo.startswith('bar'):

錯誤示范

if foo[:3] == 'bar':

1.15 文件和 sockets

1.15.1 【必須】 在文件和 sockets 結(jié)束時,顯式的關(guān)閉它。

除文件外, sockets 或其他類似文件的對象在沒有必要的情況下打開,會有許多副作用,例如:

  1. 它們可能會消耗有限的系統(tǒng)資源,如文件描述符。如果這些資源在使用后沒有及時歸還系統(tǒng),那么用于處理這些對象的代碼會將資源消耗殆盡。
  2. 持有文件將會阻止對于文件的其他諸如移動、刪除之類的操作。
  3. 僅僅是從邏輯上關(guān)閉文件和 sockets,那么它們?nèi)匀豢赡軙黄涔蚕淼某绦蛟跓o意中進行讀或者寫操作。只有當(dāng)它們真正被關(guān)閉后,對于它們嘗試進行讀或者寫操作將會拋出異常,并使得問題快速顯現(xiàn)出來。

而且,幻想當(dāng)文件對象析構(gòu)時,文件和 sockets 會自動關(guān)閉,試圖將文件對象的生命周期和文件的狀態(tài)綁定在一起的想法,都是不現(xiàn)實的。因為有如下原因:

  1. 沒有任何方法可以確保運行環(huán)境會真正的執(zhí)行文件的析構(gòu)。不同的 Python 實現(xiàn)采用不同的內(nèi)存管理技術(shù),比如延時垃圾處理機制。延時垃圾處理機制可能會導(dǎo)致對象生命周期被任意無限制的延長。
  2. 對于文件意外的引用,會導(dǎo)致對于文件的持有時間超出預(yù)期 (比如對于異常的跟蹤,包含有全局變量等)。

1.15.2 【推薦】 推薦使用 with 語句管理文件。

with open('hello.txt'as hello_file:
    for line in hello_file:
        print(line)

對于不支持使用 with 語句的類似文件的對象,使用 contextlib.closing()

import contextlib

with contextlib.closing(urllib.urlopen('http://www./')) as front_page:
    for line in front_page:
        print(line)

Legacy AppEngine 中 Python 2.5 的代碼如使用 with 語句,需要添加 from __future__ import with_statement.

1.16 訪問控制

在 Python 中,對于瑣碎又不太重要的訪問函數(shù),應(yīng)該直接使用公有變量來取代它們,這樣可以避免額外的函數(shù)調(diào)用開銷。當(dāng)添加更多功能時,也可以用屬性 (property) 來保持語法的一致性。

1.16.1 【推薦】 如果訪問屬性后需要復(fù)雜的邏輯處理,或者變量的訪問開銷很顯著, 那么應(yīng)該使用像 get_foo()set_foo() 這樣的函數(shù)調(diào)用。如果之前的代碼行為已經(jīng)通過屬性 (property) 訪問,那么就不要將新的訪問函數(shù)與屬性綁定。否則,任何試圖通過老方法訪問變量的代碼就沒法運行,使用者也就會意識到復(fù)雜性發(fā)生了變化。(如果可以重構(gòu)這個代碼是最好的了)

1.17 Main

即使是一個打算被用作腳本的文件,也應(yīng)該是可導(dǎo)入的。并且簡單的導(dǎo)入不應(yīng)該導(dǎo)致這個腳本的主功能 (main functionality) 被執(zhí)行,這是一種副作用。主功能應(yīng)該放在一個 main () 函數(shù)中。

1.17.1 【必須】 所有的文件都應(yīng)該可以被導(dǎo)入。對不需要作為程序入口地方添加 if __name__ == '__main__' 。

在 Python 中,pydoc 以及單元測試要求模塊必須是可導(dǎo)入的。你的代碼應(yīng)該在執(zhí)行主程序前總是檢查 if __name__ == '__main__' , 這樣當(dāng)模塊被導(dǎo)入時主程序就不會被執(zhí)行。所有的頂級代碼在模塊導(dǎo)入時都會被執(zhí)行。要小心不要去調(diào)用函數(shù),創(chuàng)建對象,或者執(zhí)行那些不應(yīng)該在使用 pydoc 時執(zhí)行的操作。

正確示范

def main():
      ...

if __name__ == '__main__':
    main()

1.18 命名

module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.

應(yīng)該避免的名稱:

  1. 單字符名稱,除了計數(shù)器和迭代器。
  2. 包 / 模塊名中的連字符 (-)。
  3. 雙下劃線開頭并結(jié)尾的名稱 (Python 保留,例如__init__)。

1.18.1 【推薦】 命名約定規(guī)則如下:pylint:invalid-name.

  1. 所謂內(nèi)部(Internal) 表示僅模塊內(nèi)可用,或者,在類內(nèi)是保護或私有的。
  2. 用單下劃線 (_) 開頭表示模塊變量或函數(shù)是 protected 的 (使用 from module import * 時不會包含)。
  3. 用雙下劃線 (__) 開頭的實例變量或方法表示類內(nèi)私有。
  4. 將相關(guān)的類和頂級函數(shù)放在同一個模塊里。不像 Java,沒必要限制一個類一個模塊。
  5. 對類名使用大寫字母開頭的單詞 (如 CapWords, 即 Pascal 風(fēng)格),但是模塊名應(yīng)該用小寫加下劃線的方式 (如 lower_with_under.py)。盡管已經(jīng)有很多現(xiàn)存的模塊使用類似于 CapWords.py 這樣的命名,但現(xiàn)在已經(jīng)不鼓勵這樣做,因為如果模塊名碰巧和類名一致,這會讓人困擾。

Python 之父 Guido 推薦的規(guī)范:

TypePublicInternal
Moduleslower_with_under_lower_with_under
Packageslower_with_under
ClassesCapWords_CapWords
ExceptionsCapWords
Functionslower_with_under()_lower_with_under()
Global/Class ConstantsCAPS_WITH_UNDER_CAPS_WITH_UNDER
Global/Class Variableslower_with_under_lower_with_under
Instance Variableslower_with_under_lower_with_under (protected) or __lower_with_under (private)
Method Nameslower_with_under()_lower_with_under() (protected) or __lower_with_under() (private)
Function/Method Parameterslower_with_under
Local Variableslower_with_under

二、編碼規(guī)范

2.1 三目運算符

2.1.1 【必須】 三目操作符判斷,python 不支持三目運算符,但可使用如下方式,禁止使用復(fù)雜難懂的邏輯判斷。

正確示范

x = a if a >= b else b

錯誤示范

x = a >= b and a or b

2.2 None 條件的判斷

2.2.1 【必須】 為提升可讀性,在判斷條件中應(yīng)使用 is not,而不使用 not ... is。pycodestyle:E714 test for object identity should be 'is not'.

正確示范

if foo is not None:

錯誤示范

if not foo is None:

2.3 lambda 匿名函數(shù)

2.3.1 【必須】 使用 def 定義簡短函數(shù)而不是使用 lambda。pycodestyle:E731 do not assign a lambda expression, use a def.

使用 def 的方式有助于在 trackbacks 中打印有效的類型信息,明確使用 f 函數(shù)而不是一個 lambda 的調(diào)用。

正確示范

def f(x):
    return 2 * x

錯誤示范

f = lambda x: 2 * x

2.4 異常

2.4.1 【必須】 異常類繼承自 Exception,而不是 BaseException。

2.4.2 【必須】 使用新版本拋出異常的方式,禁止使用廢棄的方式。pycodestyle:W602 deprecated form of raising exception.

正確示范

raise ValueError('message')

錯誤示范

raise ValueError, 'message'

2.4.3 【必須】 捕獲異常時,需要指明具體異常,而不是捕獲所有異常。除非已經(jīng)在當(dāng)前線程的最外層(記得還是要打印一條 traceback)。pylint:broad-except, bare-except.

正確示范

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

try:
    do_something()
except Exception as ex:  # pylint: disable=broad-except
    log.exception(ex)  # 應(yīng)將錯誤堆棧打印到日志文件中,以供后續(xù)排查。
    handle_exception_or_not()  # 除打印日志外,還可以進一步處理如清理資源等,可選。

錯誤示范

try:
    import platform_specific_module
except Exception:  # broad-except
    platform_specific_module = None

try:
    do_something()
except Exception:  # 框架等未明確異常場景,建議增加 traceback 打印
    pass

2.4.4 【推薦】 建議在代碼中用異常替代函數(shù)的錯誤返回碼。

正確示范

def write_data():
    if check_file_exist():
        do_something()
    else:
        raise FileNotExist()

錯誤示范

def write_data():
    if check_file_exist():
        do_something()
        return 0
    else:
        return FILE_NOT_EXIST

2.4.5 【推薦】except 子句中重新拋出原有異常時,不能用 raise ex,而是用 raise。

正確示范

try:
    raise MyException()
except MyException as ex:
    try_handle_exception()
    raise  # 可以保留原始的 traceback


try:
    raise MyException()
except MyException as ex:
    log.exception(ex)
    raise AnotherException(str(ex))  # 允許的,建議保留好之前的異常棧信息,用于定位問題

錯誤示范

try:
    raise MyException()
except MyException as ex:
    try_handle_exception()
    raise ex  # 異常棧信息從這里開始,原始的raise異常棧信息消息

2.4.6 【推薦】 所有 try/except 子句的代碼要盡可的少,以免屏蔽其他的錯誤。

正確示范

try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

錯誤示范

try:
    # 范圍太廣
    return handle_value(collection[key])
except KeyError:
    # 會捕捉到 handle_value() 中的 KeyError
    return key_not_found(key)

2.5 條件表達式

2.5.1 【推薦】 函數(shù)或者方法在沒有返回時要明確返回 None。pylint:inconsistent-return-statements.

正確示范

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)

錯誤示范

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

2.5.2 【推薦】 對于未知的條件分支或者不應(yīng)該進入的分支,建議拋出異常,而不是返回一個值(比如說 NoneFalse)。

正確示范

def f(x):
    if x in ('SUCCESS',):
        return True
    else:
        raise MyException()  # 如果一定不會走到的條件,可以增加異常,防止將來未知的語句執(zhí)行。

錯誤示范

def f(x):
    if x in ('SUCCESS',):
        return True
    return None

2.5.3 【可選】 ifelse 盡量一起出現(xiàn),而不是全部都是 if 子句。

正確示范

if condition:
    do_something()
else:
    # 增加說明注釋
    pass

if condition:
    do_something()
# 這里增加注釋說明為什么不用寫else子句
# else:
#     pass

錯誤示范

if condition:
    do_something()
if another_condition:  # 不能確定是否筆誤為 elif ,還是開啟全新一個if條件
    do_another_something()
else:
    do_else_something()

2.6 True/False 布爾運算

2.6.1 【必須】 不要用 ==True、 False 進行布爾運算。pylint:singleton-comparison.

正確示范

if greeting:
   pass

錯誤示范

if greeting == True:
   pass

if greeting is True:  # Worse
   pass

2.6.2 【必須】 對序列(字符串、列表 、元組),空序列為 false 的情況。pylint:len-as-condition.

正確示范

if not seq:
   pass

if seq:
   pass

錯誤示范

if len(seq):
   pass

if not len(seq):
   pass

2.7 列表推導(dǎo)式

2.7.1 【必須】 禁止超過 1 個 for 語句或過濾器表達式,否則使用傳統(tǒng) for 循環(huán)語句替代。

正確示范

number_list = [123102055]
odd = [i for i in number_list if i % 2 == 1]

result = []
for x in range(10):
    for y in range(5):
        if x * y > 10:
            result.append((x, y))

錯誤示范

result = [(x, y) for x in range(10for y in range(5if x * y > 10]  # for 語句

2.7.2 【推薦】 列表推導(dǎo)式適用于簡單場景。如果語句過長,每個部分應(yīng)該單獨置于一行:映射表達式, for 語句, 過濾器表達式。

正確示范

fizzbuzz = []
for n in range(100):
    if n % 3 == 0 and n % 5 == 0:
        fizzbuzz.append(f'fizzbuzz {n}')
    elif n % 3 == 0:
        fizzbuzz.append(f'fizz {n}')
    elif n % 5 == 0:
        fizzbuzz.append(f'buzz {n}')
    else:
        fizzbuzz.append(n)


for n in range(111):
    print(n)

錯誤示范

# 條件過于復(fù)雜,應(yīng)該采用for語句展開
fizzbuzz = [
    f'fizzbuzz {n}' if n % 3 == 0 and n % 5 == 0
    else f'fizz {n}' if n % 3 == 0
    else f'buzz {n}' if n % 5 == 0
    else n
    for n in range(100)
]


[print(n) for n in range(111)]  # 無返回值

2.8 函數(shù)

2.8.1 【必須】 模塊內(nèi)部禁止定義重復(fù)函數(shù)聲明。pylint:function-redefined.

禁止重復(fù)的函數(shù)定義。

錯誤示范

def get_x(x):
    return x

def get_x(x):  # 模塊內(nèi)重復(fù)定義
    return x

2.8.2 【必須】 函數(shù)參數(shù)中,不允許出現(xiàn)可變類型變量作為默認(rèn)值。pylint:dangerous-default-value.

正確示范

def f(x=0, y=None, z=None):
    if y is None:
        y = []
    if z is None:
        z = {}

錯誤示范

def f(x=0, y=[], z={}):
    pass

def f(a, b=time.time()):
    pass

2.9 變量

2.9.1 【必須】 禁止定義了變量卻不使用它。pylint:unused-variable.

在代碼里到處定義變量卻沒有使用它,不完整的代碼結(jié)構(gòu)看起來像是個代碼錯誤。即使沒有使用,但是定義變量仍然需要消耗資源,并且對閱讀代碼的人也會造成困惑,不知道這些變量是要做什么的。

正確示范

def get_x_plus_y(x, y):
    return x + y

錯誤示范

some_unused_var = 42

# 定義了變量不意味著就是使用了
y = 10
y = 5

# 對自身的操作并不意味著使用了
z = 0
z = z + 1

# 未使用的函數(shù)參數(shù)
def get_x(x, y):
    return x

2.9.2 【推薦】 使用雙下劃線 __ 來代表不需要的變量,單下劃線 _ 容易與 gettext() 函數(shù)的別名沖突。

正確示范

path = '/tmp/python/foobar.txt'
dir_name, __ = os.path.split(path)

工具與配置

flake8

flake8 是一個結(jié)合了 pycodestyle,pyflakes,mccabe 檢查 Python 代碼規(guī)范的工具。

使用方法:

flake8 {source_file_or_directory}

在項目中創(chuàng)建 setup.cfg 或者 tox.ini 或者 .flake8 文件,添加 [flake8] 部分。

推薦的配置文件如下:

[flake8]
ignore =
;W503 line break before binary operator
W503,
;E203 whitespace before ':'
E203,

; exclude file
exclude =
.tox,
.git,
__pycache__,
build,
dist,
*.pyc,
*.egg-info,
.cache,
.eggs

max-line-length = 120

如果需要屏蔽告警可以增加行內(nèi)注釋 # noqa,例如:

example = lambda'example'  # noqa: E731

pylint

pylint 是一個能夠檢查編碼質(zhì)量、編碼規(guī)范的工具。

配置項較多,單獨一個配置文件配置,詳情可查閱:.pylintrc

使用方法:

pylint {source_file_or_directory}

如果遇到一些實際情況與代碼沖突的,可以在行內(nèi)禁用相關(guān)檢查,例如 :

try:
    do_something()
except Exception as ex:  # pylint: disable=broad-except
    pass

如果需要對多行的進行禁用規(guī)則,可以配套使用 # pylint: disable=具體錯誤碼 /# pylint: enable=具體錯誤碼。

# pylint: disable=invalid-name
這里的代碼塊會被忽略相關(guān)的告警
app = Flask(__name__)
# pylint: enable=invalid-name

black

black 是一個官方的 Python 代碼格式化工具。

使用方法:

black {source_file_or_directory}

如果不想格式化部分代碼,可以配套使用 # fmt: off/# fmt: on 臨時關(guān)閉格式化。

# fmt: off
在這的代碼不會被格式化
# fmt: on

EditorConfig

EditorConfig 可以幫助開發(fā)同一項目下的跨多 IDE 的開發(fā)人員保持一致編碼風(fēng)格。

在項目的根目錄下放置.editorconfig文件,可以讓編輯器規(guī)范文件對格式。參考配置如下:

# https://

root = true

[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf

[*.py]
max_line_length = 120

[*.bat]
indent_style = tab
end_of_line = crlf

[LICENSE]
insert_final_newline = false

[Makefile]
indent_style = tab

支持常見的 IDE ,配置說明及 IDE 的支持情況可參考官網(wǎng) 。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日本熟妇五十一区二区三区| 少妇人妻一级片一区二区三区| 国产欧美日韩精品一区二区| 久久精品国产一区久久久| 人人妻人人澡人人夜夜| 丰满少妇被猛烈插入在线观看| 美女被后入福利在线观看| 国产精品亚洲精品亚洲| 久久热在线视频免费观看| 日韩少妇人妻中文字幕| 亚洲精品中文字幕熟女| 91亚洲国产成人久久| 六月丁香六月综合缴情| 91欧美激情在线视频| 亚洲中文字幕在线视频频道| 国产综合一区二区三区av| 亚洲欧美日韩熟女第一页| 国产情侣激情在线对白| 国产一区二区三区成人精品| 免费一区二区三区少妇| 国产日韩久久精品一区| 国产精品99一区二区三区| 又色又爽又黄的三级视频| 午夜福利视频日本一区| 视频一区中文字幕日韩| 色婷婷中文字幕在线视频| 精产国品一二三区麻豆| 国内欲色一区二区三区| 欧美日韩国产免费看黄片| 欧美日韩国产一级91| 日韩无套内射免费精品| 在线日本不卡一区二区| 久久精品国产亚洲av久按摩| 精品人妻精品一区二区三区| 亚洲a码一区二区三区| 国产熟女一区二区三区四区| 国产精品欧美日韩中文字幕| 99久久国产精品亚洲| 欧洲日本亚洲一区二区| 国产一级特黄在线观看| 日本深夜福利视频在线|