前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文弄懂Python代码规范

一文弄懂Python代码规范

作者头像
用户3578099
发布2023-12-11 13:36:20
1580
发布2023-12-11 13:36:20
举报
文章被收录于专栏:AI科技时讯AI科技时讯

Python代码规范pep8

Author: Guido van Rossum, Barry Warsaw, Alyssa Coghlan Status: Active Type: Process Created: 05-Jul-2001 Post-History: 05-Jul-2001, 01-Aug-2013

Introduction|简介

这份文档为主Python发行版中标准库的Python代码提供了编码规范。请参阅相关的信息性PEP,该PEP描述了Python C实现中的C代码的样式指南。

这份文档和PEP 257(文档字符串规范)改编自Guido的原始Python样式指南文章,并加入了Barry样式指南的一些内容[2]。

随着额外的约定的发现和语言本身的变化使过去的约定变得过时,这个样式指南会随着时间的推移而演变。

许多项目都有自己的编码风格指南。在任何冲突的情况下,项目特定的指南优先适用于该项目。

A Foolish Consistency is the Hobgoblin of Little Minds |愚蠢的一贯性是小心灵的小妖精

Guido的一个关键洞察是代码被阅读的频率远远超过它被编写的次数。这里提供的准则旨在提高代码的可读性,并使其在广泛的Python代码范围内保持一致。正如PEP 20Python之禅所说:“可读性很重要”。

代码语言:javascript
复制
import this

"""
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""
"""
美胜过丑陋。
明确胜过隐晦。
简单胜过复杂。
复杂胜过繁琐。
扁平胜过嵌套。
疏松胜过密集。
可读性很重要。
特殊情况并不足以打破规则。
虽然实用性胜过纯粹性。
错误永远不应该悄悄地过去。
除非明确沉默。
面对不确定性,拒绝猜测的诱惑。
应该有一种明显的方式来做事,最好只有一种。
尽管这种方式一开始可能不明显,除非你是荷兰人(指Python的创作者Guido van Rossum,他是荷兰人)。
现在胜过永远不做。
尽管从不做通常比立即去做好。
如果实现很难解释,那是一个坏主意。
如果实现容易解释,那可能是个好主意。
命名空间是一个绝妙的主意 - 让我们做更多这样的事情!
"""

样式指南关乎一致性。与这个样式指南保持一致是重要的。项目内的一致性更为重要。在一个模块或函数内的一致性是最重要的。

然而,要知道何时要有所不一致 - 有时样式指南的建议并不适用。当有疑虑时,请运用您的最佳判断力。查看其他示例并决定哪种看起来最好。并且不要犹豫询问!

特别是:不要为了遵循这个PEP而破坏向后兼容性

忽略特定指南的一些好理由包括:

  • 当应用该指南会使代码对于习惯于阅读遵循这个PEP的代码的人来说变得更不可读时。
  • 为了与周围的代码保持一致,而周围的代码也违反了它(也许是由于历史原因) - 尽管这也是清理其他人犯下的错误的机会(真正的XP风格)。
  • 因为所讨论的代码在该指南引入之前就存在,而且修改该代码没有其他原因。
  • 当代码需要保持与不支持样式指南建议的功能的较旧版本的Python兼容时。

Code Lay-out|代码布局

Indentation|缩进

使用每个缩进级别4个空格。

连续行应该使用垂直对齐括号、方括号和花括号内的元素,可以使用Python的括号内隐式行连接,也可以使用悬挂缩进 [1]。使用悬挂缩进时,应考虑以下事项:第一行不应有参数,并且应使用进一步的缩进清晰地表示它是一行的延续

代码语言:javascript
复制
# 正确:

# 与开始分隔符对齐
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)

# 错误:

# 在不使用垂直对齐时,禁止在第一行放置参数
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# 由于缩进不可区分,需要进一步的缩进
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

4个空格的规则在连续行中是可选的。

可选:

代码语言:javascript
复制
# 悬挂缩进可以缩进到除了4个空格之外的其他位置
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

当if语句的条件部分足够长,需要跨多行编写时,值得注意的是,两个字符的关键字(即 if),加上一个空格,再加上一个开括号,会为多行条件的后续行创建一个自然的4个空格缩进。这可能会在if语句内嵌的缩进代码块的可视上产生冲突,后者也会自然地缩进到4个空格。这个PEP对如何(或是否)进一步在视觉上区分这样的条件行与if语句内嵌的代码块采取了明确的立场。在这种情况下可以接受的选项包括但不限于:

代码语言:javascript
复制
# 不需要额外的缩进
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 添加注释,在支持语法高亮的编辑器中提供一些区分
if (this_is_one_thing and
    that_is_another_thing):
    # 由于两个条件都为真,可以执行某些操作.
    do_something()

# 在条件的连续行上添加一些额外的缩进
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

多行结构的闭括号/方括号/括号可以与列表的最后一行的第一个非空格字符对齐,如下所示:

代码语言:javascript
复制
my_list = [
    1, 2, 3,
    4, 5, 6,
]

result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)
Tabs or Spaces|制表符还是空格

空格是首选的缩进方法。 制表符应仅用于与已使用制表符缩进的代码保持一致。 Python 不允许混合制表符和空格进行缩进

Maximum Line Length|最大代码行长度

限制所有行的最大长度为79个字符

对于较少结构限制的长文本块(例如文档字符串或注释),行长度应限制为72个字符

限制所需的编辑器窗口宽度可以使多个文件并排打开,并在使用代码审查工具时表现良好,这些工具将两个版本呈现在相邻的列中。

大多数工具中的默认换行破坏了代码的视觉结构,使其更难理解。这些限制的选择是为了避免在将窗口宽度设置为80的编辑器中换行,即使工具在换行时在最后一列放置标记符号。有些基于Web的工具可能根本不提供动态换行。

一些团队更喜欢更长的行长度。对于由能够就此问题达成一致的团队专门或主要维护的代码,可以将行长度限制提高到99个字符,前提是注释和文档字符串仍然在72个字符处换行

Python标准库保守并要求将行限制为79个字符(将文档字符串/注释限制为72个字符)。

首选的长行换行方式是使用Python在括号、方括号和大括号内隐式的行连续。可以通过在括号内包装表达式来将长行分成多行。这应该优先于使用反斜杠进行行连续。

反斜杠在某些情况下仍然是适当的。例如,在Python 3.10之前,长的多个with语句无法使用隐式连续,因此在那种情况下使用反斜杠是可以接受的:

代码语言:javascript
复制
with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())
Should a Line Break Before or After a Binary Operator|应该在二元运算符之前还是之后换行

几十年来,推荐的风格是在二元运算符之后换行。但这样做可能会影响可读性,有两方面的原因:运算符往往分散在屏幕上的不同列,而且每个运算符都移动到其操作数的前一行。在这里,眼睛需要额外的工作来判断哪些项目是相加的,哪些是相减的:

代码语言:javascript
复制
# 错误:
# 运算符远离操作数
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

为了解决这个可读性问题,数学家及其出版商遵循相反的约定。唐纳德·克努斯在他的《计算机排版》系列中解释了传统规则:“虽然段落内的公式总是在二元运算符和关系之后换行,但显示的公式总是在二元运算符之前换行”[3]。

遵循数学传统通常会产生更易读的代码:

代码语言:javascript
复制
# 正确
# 易于将运算符与操作数匹配
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

在 Python 代码中,只要约定在本地一致,就允许在二元运算符之前或之后中断。对于新代码,建议采用 Knuth 风格。

Blank Lines|空白行

在顶层函数和类定义之间留两个空行

类内的方法定义周围留有一行空行

可以适度使用额外的空行来分隔相关函数的组。在一组相关的一行函数之间可以省略空行(例如一组虚拟实现)。

在函数中,适度地使用空行表示逻辑部分。

Python将控制-L(即^L)换页符视为空白字符;许多工具将这些字符视为分页符,因此可以使用它们来分隔文件中相关部分的页面。请注意,一些编辑器和基于Web的代码查看器可能无法将控制-L识别为换页符,并将显示其位置的另一字符。

代码语言:javascript
复制
# 顶层函数之间的两个空行
def function1():
    pass

def function2():
    pass


# 两个空行分隔顶层函数和类定义
class MyClass:
    
    # 类内方法之间的一个空行
    def method1(self):
        pass
    
    def method2(self):
        pass

# 额外的空行分隔相关函数组
def related_function1():
    pass

def related_function2():
    pass

# 函数内逻辑部分使用适度的空行
def complex_function():
    # 逻辑部分1
    x = 1
    y = 2
    
    # 空行分隔逻辑部分
    result = x + y
    
    # 逻辑部分2
    return result
Source File Encoding|源文件编码

核心Python发行版中的代码应始终使用UTF-8,并且不应具有编码声明

在标准库中,仅应出于测试目的使用非UTF-8编码。请谨慎使用非ASCII字符,最好仅用于表示地点和人名。如果将非ASCII字符用作数据,请避免嘈杂的Unicode字符,例如z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘和字节顺序标记。

Python标准库中的所有标识符必须仅使用ASCII标识符,并且在可行的情况下应使用英语单词(在许多情况下,使用了不是英语的缩写和技术术语)。

鼓励全球受众的开源项目采用类似的政策。

Import|引入
  • 导入通常应该放在单独的行上:
代码语言:javascript
复制
# Correct:
import os
import sys

# Wrong:
import sys, os

# Correct:
from subprocess import Popen, PIPE
  • 导入总是放在文件顶部,紧接着任何模块注释和文档字符串之后,而在模块全局变量和常量之前。导入应按照以下顺序分组:
    • 标准库导入
    • 相关的第三方导入
    • 本地应用/库特定导入

在每组导入之间应该留有一行空行。

代码语言:javascript
复制
# 标准库导入
import os
import sys

# 第三方库导入
import requests
from flask import Flask

# 本地应用/库导入
from myapp import utils
from myapp.models import User

# 模块全局变量
MY_CONSTANT = 42

# 函数定义等其他代码...
  • 建议使用绝对导入,因为它们通常更易读,而且如果导入系统配置错误(例如,包内的目录出现在 sys.path 中时),它们通常表现得更好(或者至少提供更好的错误消息):
代码语言:javascript
复制
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

然而,相对导入也是绝对导入的可接受替代方案,特别是在处理复杂的包布局时,其中使用绝对导入会显得不必要冗长:

代码语言:javascript
复制
from . import sibling
from .sibling import example

标准库代码应避免复杂的包布局并始终使用绝对导入

  • 从包含类的模块中导入类时,通常可以这样书写:
代码语言:javascript
复制
from myclass import MyClass
from foo.bar.yourclass import YourClass

如果这种拼写导致本地名称冲突,请明确拼写它们:

代码语言:javascript
复制
import myclass
import foo.bar.yourclass

后续使用myclass.MyClassfoo.bar.yourclass.YourClass

  • 应避免使用通配符导入(fromimport *),因为它们会使命名空间中存在哪些名称变得不清晰,既会困扰读者,也会困扰许多自动化工具。通配符导入有一种可以辩解的用例,即将内部接口作为公共API的一部分重新发布(例如,用加速模块的定义覆盖纯Python实现的接口,事先不知道哪些定义将被覆盖)。以这种方式重新发布名称时,仍然适用以下关于公共接口和内部接口的准则。
Module Level Dunder Names|模块级别的双下划线命名

模块级别的“dunders”(即具有两个前导和两个尾随下划线的名称),例如__all____author____version__等,应该放在模块docstring之后,但在除了__future__导入之外的任何导入语句之前。Python要求未来的导入必须出现在模块中除了文档字符串之外的任何其他代码之前:

代码语言:javascript
复制
"""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

String Quotes|字符串引号

在Python中,单引号和双引号括起来的字符串是相同的。PEP 8并未就此提出建议。选择一种规则并坚持使用它。但是,当字符串包含单引号或双引号字符时,建议使用另一种引号,以避免在字符串中使用反斜杠,这有助于提高可读性

对于三引号括起来的字符串,始终使用双引号字符,以保持与PEP 257中文档字符串约定的一致性。

One-line Doctrings| 一行文档字符串

单行代码应该是非常明显和简短的情况。它们真的应该适合一行,例如:

代码语言:javascript
复制
def kos_root():
    """Return the pathname of the KOS root directory."""
    global _kos_root
    if _kos_root: return _kos_root
    ...

注意:

即使字符串适合一行,也使用三重引号。这样可以方便以后扩展它。

结束引号与开始引号在同一行。这在一行中看起来更好。

在文档字符串之前或之后都没有空行。

文档字符串是一个以句点结束的短语。它规定了函数或方法的效果作为命令(“执行这个”,“返回那个”),而不是作为描述;例如,不要写“返回路径名...”。

单行文档字符串不应该是“签名”,重申函数/方法的参数(可以通过内省获取)。不要这样做:

代码语言:javascript
复制
def function(a, b):
    """function(a, b) -> list"""

这种类型的文档字符串只适用于C函数(如内置函数),其中无法进行内省。然而,通过内省无法确定返回值的性质,因此应该提到。对于这样的文档字符串,首选形式可能是:

代码语言:javascript
复制
def function(a, b):
    """Do X and return a list."""
Multi-line Docstrings|多行文档字符串

多行文档字符串包括一行摘要,就像单行文档字符串一样,后跟一行空行,然后是更详细的描述。摘要行可能会被自动索引工具使用;重要的是它适合在一行上,并且与文档字符串的其余部分由一行空行分隔。摘要行可以与开头引号位于同一行,也可以在下一行。整个文档字符串的缩进与其第一行的引号相同。

在所有记录类的文档字符串(单行或多行)之后插入一行空行 - 一般来说,类的方法彼此之间由单个空行分隔,并且文档字符串需要与第一个方法通过一行空行分隔。

脚本(独立程序)的文档字符串应该可用作其“用法”消息,在以不正确或缺少参数调用脚本时打印此消息(或者使用“-h”选项,表示“帮助”)。这样的文档字符串应该记录脚本的功能和命令行语法、环境变量和文件。用法消息可以相当详细(几屏幕),应足够供新用户正确使用命令,以及对于熟练用户来说是所有选项和参数的完整快速参考。

模块的文档字符串通常应列出由模块导出的类、异常和函数(以及任何其他对象),每个对象都有一行摘要。(这些摘要通常比对象文档字符串中的摘要行提供更少的细节。)包的文档字符串(即包的__init__.py模块的文档字符串)还应列出由包导出的模块和子包。

函数或方法的文档字符串应概述其行为并记录其参数、返回值、副作用、引发的异常以及调用时的限制(如果适用)。应标明可选参数。文档字符串应说明关键字参数是否是接口的一部分。

类的文档字符串应概述其行为并列出公共方法和实例变量。如果类意图被子类化,并且有一个供子类使用的附加接口,这个接口应该单独列在文档字符串中。类的构造函数应该在其__init__方法的文档字符串中记录。各个方法应该由各自的文档字符串记录。

如果一个类是另一个类的子类,并且其行为主要继承自那个类,其文档字符串应提到这一点,并概述两者之间的区别。使用动词“override”表示子类方法替换了超类方法并且不调用超类方法;使用动词“extend”表示子类方法调用超类方法(除了自己的行为)。

不要使用Emacs的约定,即在运行文本中将函数或方法的参数写成大写。Python区分大小写,参数名可用于关键字参数,因此文档字符串应记录正确的参数名。最好在单独的行上列出每个参数。例如:

代码语言:javascript
复制
def complex(real=0.0, imag=0.0):
    """Form a complex number.

    Keyword arguments:
    real -- the real part (default 0.0)
    imag -- the imaginary part (default 0.0)
    """
    if imag == 0.0 and real == 0.0:
        return complex_zero
    ...
代码语言:javascript
复制
class ExampleClass:
    """This is a brief summary of what the class does.

    Public Methods:
    - public_method1: Explanation of public_method1.
    - public_method2: Explanation of public_method2.

    Instance Variables:
    - instance_var1: Explanation of instance_var1.
    - instance_var2: Explanation of instance_var2.

    Public Subclass Interface:
    - subclass_method: Explanation of subclass_method.
    """

    def __init__(self, arg1, arg2, kwarg1='default'):
        """This is a brief summary of what the constructor does.

        Parameters:
        - arg1: Explanation of arg1.
        - arg2: Explanation of arg2.
        - kwarg1 (optional): Explanation of kwarg1, which has a default value.
        """
        # Constructor implementation here

    def public_method1(self, param):
        """This is a brief summary of what public_method1 does.

        Parameters:
        - param: Explanation of param.

        Returns:
        Explanation of the return value.
        """
        # Method implementation here

    def public_method2(self):
        """This is a brief summary of what public_method2 does.

        Returns:
        Explanation of the return value.
        """
        # Method implementation here
Handling Docstring Indentation|处理文档字符串缩进

文档字符串处理工具将从文档字符串的第二行及以后的所有行中剥离一个统一的缩进,该缩进等于第一行之后所有非空行的最小缩进。文档字符串的第一行(即直到第一个换行符之前)中的任何缩进都是无关紧要的并将被移除。保留文档字符串后续行的相对缩进。应该从文档字符串的开头和结尾删除空行。

代码语言:javascript
复制
def trim(docstring):
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxsize
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxsize:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)

在这个示例中,文档字符串包含两个换行符,因此有 3 行长。第一行和最后一行是空行:

代码语言:javascript
复制
def foo():
    """
    This is the second line of the docstring.
    """

代码语言:javascript
复制
>>> print repr(foo.__doc__)
'\n    This is the second line of the docstring.\n    '
>>> foo.__doc__.splitlines()
['', '    This is the second line of the docstring.', '    ']
>>> trim(foo.__doc__)
'This is the second line of the docstring.'

Whitespace in Expressions and Statements|表达式和语句中的空格

Pet Peeves|令人讨厌的代码习惯
  • 在以下情况下避免使用多余的空格:
代码语言:javascript
复制
# Correct:
spam(ham[1], {eggs: 2})

# Wrong:
spam( ham[ 1 ], { eggs: 2 } )
  • 在尾随逗号和后面的右括号之间:
代码语言:javascript
复制
# Correct:
foo = (0,)

# Wrong:
bar = (0, )
  • 紧接在逗号、分号或冒号之前:
代码语言:javascript
复制
# Correct:
if x == 4: print(x, y); x, y = y, x
    
# Wrong:
if x == 4 : print(x , y) ; x , y = y , x
  • 然而,在切片中,冒号的作用类似于二进制运算符,应该在两侧具有相等的空格量(将其视为优先级最低的运算符)。在扩展切片中,两个冒号必须应用相同数量的空格。例外情况:当省略切片参数时,空格也会被省略
代码语言:javascript
复制
# Correct:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]

# Wrong:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : step]
ham[ : upper]
  • 紧接在开始函数调用的参数列表的左括号之前:
代码语言:javascript
复制
# Correct:
spam(1)

# Wrong:
spam (1)
  • 紧接在开始索引或切片的左括号之前:
代码语言:javascript
复制
# Correct:
dct['key'] = lst[index]

# Wrong:
dct ['key'] = lst [index]
  • 在赋值(或其他)运算符周围使用多个空格,以使其与另一个对齐
代码语言:javascript
复制
# Correct:
x = 1
y = 2
long_variable = 3

# Wrong:
x             = 1
y             = 2
long_variable = 3
Other Recommendations|其他推荐
  • 避免在任何地方留下尾随的空格。因为它通常是看不见的,所以可能会引起混淆:例如,反斜杠后面跟着一个空格和一个换行符不算作行连续标记。有些编辑器不会保留它,而且许多项目(如CPython本身)都有预提交挂钩来拒绝它。
  • 始终在这些二进制运算符的两侧各用一个空格包围:赋值(=),增强赋值(+=,-=等),比较(==,<,>,!=,<>,<=,>=,in,not in,is,is not),布尔值(and,or,not)。
  • 如果使用具有不同优先级的运算符,请考虑在具有最低优先级的运算符周围添加空格。请根据自己的判断力行事;但是,永远不要使用多个空格,并且在二进制运算符的两侧始终具有相同数量的空格
代码语言:javascript
复制
# Correct:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

# Wrong:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
  • 函数注释应该使用冒号的正常规则,并且如果存在 -> 箭头,则始终在其周围加上空格。 (有关函数注释的更多信息,请参见下面的“函数注释”:
代码语言:javascript
复制
# Correct:
def munge(input: AnyStr): ...
def munge() -> PosInt: ...

# Wrong:
def munge(input:AnyStr): ...
def munge()->PosInt: ...
  • 在用于指示关键字参数或用于指示未注释的函数参数的默认值时,不要在 = 符号周围使用空格:
代码语言:javascript
复制
# Correct:
def complex(real, imag=0.0):
    return magic(r=real, i=imag)

# Wrong:
def complex(real, imag = 0.0):
    return magic(r = real, i = imag)

然而,在将参数注释与默认值结合使用时,在 = 符号周围使用空格

代码语言:javascript
复制
# Correct:
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...

# Wrong:
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...
  • 复合语句(同一行上的多个语句)通常是不鼓励的
代码语言:javascript
复制
# Correct:
if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()

# Wrong:
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
  • 虽然有时将具有小主体的 if/for/while 语句放在同一行上是可以的,但永远不要对多子句语句这样做。还要避免折叠这样的长行!
代码语言:javascript
复制
# Wrong:
if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()
    
# Wrong:
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()

try: something()
finally: cleanup()

do_one(); do_two(); do_three(long, argument,
                             list, like, this)

if foo == 'blah': one(); two(); three()

When to Use Trailing Commas|何时使用尾随逗号

尾随逗号通常是可选的,但当创建一个只有一个元素的元组时是强制的。为了清晰起见,建议将后者括在(从技术上讲是多余的)括号中:

代码语言:javascript
复制
# Correct:
FILES = ('setup.cfg',)

# Wrong:
FILES = 'setup.cfg',

当尾随逗号是多余的时候,通常在使用版本控制系统时很有帮助,当预计随时间推移将扩展值、参数或导入项的列表。模式是将每个值(等等)单独放在一行上,始终添加尾随逗号,并在下一行添加右括号/方括号/大括号。然而,在与关闭定界符相同的行上放置尾随逗号是没有意义的(除了上述单例元组的情况):

代码语言:javascript
复制
# Correct:
FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

# Wrong:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

Comments|注释

与代码相矛盾的注释比没有注释更糟糕。始终优先确保在代码更改时使注释保持最新!

注释应该是完整的句子。第一个单词应大写,除非它是以小写字母开头的标识符(永远不要更改标识符的大小写!)。

块注释通常由一个或多个段落组成,由完整的句子构建,每个句子以句点结束

在多句注释中,除了最后一句之外,句子结束后应使用一个或两个空格。

确保您的注释对使用您所编写的语言的其他人来说是清晰且容易理解的。

非英语国家的Python编码人员:请用英语编写您的注释,除非您百分之百确定代码永远不会被不懂您语言的人阅读。

Block Comments|块注释

块注释通常适用于其后的一些(或所有)代码,并缩进到与该代码相同的级别。块注释的每一行都以“#”和一个空格开头(除非它是注释内的缩进文本)。

块注释内的段落由包含单个“#”的行分隔。

代码语言:javascript
复制
# This is a block comment
# It applies to the following code block
# Each line starts with "#" and a space

# First paragraph of the comment
# providing additional context.

# Second paragraph with more details.
# Each paragraph is separated by a line starting with "#".
code_here = 42
Inline Comments|内联注释

谨慎使用内联注释。

内联注释是与语句位于同一行的注释。内联注释应与语句至少相隔两个空格。它们应以“#”和一个空格开头。

如果内联注释陈述了显而易见的事实,则它们是不必要的,实际上是分散注意力的。不要这样做:

代码语言:javascript
复制
x = x + 1                 # Increment x
x = x + 1                 # Compensate for border
Documentation Strings|文档字符串

编写良好文档字符串(即“docstrings”)的规范被概括在 PEP 257 中。

  • 为所有公共模块、函数、类和方法编写文档字符串。对于非公共方法,文档字符串并非必需,但您应该有一条注释来描述该方法的功能。这条注释应该出现在 def 行之后。
  • PEP 257 描述了良好的文档字符串约定。请注意,最重要的是,结束多行文档字符串的 """ 应该单独位于一行上:
代码语言:javascript
复制
"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""
  • 对于一行文档字符串,请确保关闭 """ 位于同一行上:
代码语言:javascript
复制
"""Return an ex-parrot."""

Naming Conventions|命名约定

Python库的命名规范有些混乱,因此永远无法完全保持一致。尽管如此,以下是目前推荐的命名标准。新模块和包(包括第三方框架)应按照这些标准编写,但如果现有库采用不同的风格,更倾向于保持内部一致性。

Overriding Principle|压到一切的原则

用户可见的作为 API 公共部分的名称应遵循反映用法而不是实现的约定。

在 Python 中,当定义作为公共 API(应用程序编程接口)一部分的名称(例如变量、函数、类等)时,遵循反映用户如何与这些名称交互的约定至关重要。这种做法可以确保清晰度、一致性,并为使用您的代码的开发人员提供更好的体验。 例如,如果您正在设计一个将成为公共 API 一部分的类,请选择一个能够向开发人员清楚传达其目的和用法的名称。该名称应该在其预期用途的上下文中有意义,并且应该遵循既定的约定,以在 Python 代码库中保持标准样式。

Descriptive: Naming Styles|描述性:命名风格

有许多不同的命名风格。能够独立于其用途识别使用了哪种命名风格将会很有帮助。

以下是常见的命名风格:

  • b(单个小写字母)
  • B(单个大写字母)
  • lowercase(小写字母)
  • lower_case_with_underscores(下划线分隔的小写字母)
  • UPPERCASE(大写字母)
  • UPPER_CASE_WITH_UNDERSCORES(下划线分隔的大写字母)
  • CapitalizedWords(或CapWords,或CamelCase,因为其字母看起来有点崎岖[4])。这有时也被称为StudlyCaps。 注意:在CapWords中使用首字母缩写时,请将缩写的所有字母都大写。因此,HTTPServerError比HttpServerError更好。
  • mixedCase(与CapitalizedWords不同之处在于以小写字母开头!)
  • Capitalized_Words_With_Underscores(丑陋!)

还有一种风格,即使用短的唯一前缀将相关的名称分组在一起。这在Python中并不常见,但出于完整性考虑提到。例如,os.stat() 函数返回一个元组,其项的传统命名为 st_modest_sizest_mtime 等。这样做是为了强调与POSIX系统调用结构的字段的对应关系,这有助于那些熟悉该结构的程序员。

X11库在其所有公共函数前使用前缀 X。在Python中,通常认为这种风格是不必要的,因为属性和方法名都带有对象前缀,而函数名都带有模块名前缀。

此外,还识别以下使用前导或尾随下划线的特殊形式(这些通常可以与任何命名约定结合使用):

  • _single_leading_underscore:弱的“内部使用”指示符。例如,from M import * 不会导入以下划线开头的对象的名称。
  • single_trailing_underscore_:按照惯例用于避免与Python关键字冲突,例如 tkinter.Toplevel(master, class_='ClassName')
  • __double_leading_underscore:在命名类属性时,调用名称修饰(在类 FooBar 中,__boo 变为 _FooBar__boo;见下文)。
  • __double_leading_and_trailing_underscore__:存在于用户可控制的命名空间中的“magic”对象或属性。例如,__init____import____file__。永远不要发明这样的名称;只能按照文档使用它们。
Prescriptive: Naming Conventions|规定性:命名约定
Names to Avoid|应避免使用的名字
  • 永远不要将字符 'l'(小写字母 "el")、'O'(大写字母 "oh")或 'I'(大写字母 "eye")作为单个字符的变量名使用
  • 在某些字体中,这些字符与数字 1 和 0 难以区分。当有使用 'l' 的冲动时,请改用 'L'。
ASCII Compatibility|ASCII 兼容性

标准库中使用的标识符必须与 ASCII 兼容,如 PEP 3131 的策略部分中所述。

保持模块与ASCII兼容。对于任何标识符命名(例如,类名或函数名),只使用ASCII字符。即使某些Unicode字符看起来像ASCII字符,也非常重要坚持使用ASCII以保持兼容性。

Package and Module Names|包和模块名称

类名通常应使用CapWords约定。

在接口被记录并主要用作可调用对象的情况下,可以使用函数的命名约定。

请注意,内置名称有一个单独的约定:大多数内置名称都是单词(或两个单词连在一起),仅在异常名称和内置常量中使用CapWords约定

Type Variable Names|类型变量名称

在PEP 484中引入的类型变量的名称通常应使用CapWords,更偏好短名称:T、AnyStr、Num。建议为用于声明协变或逆变行为的变量分别添加后缀_co或_contra:

代码语言:javascript
复制
from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
Exception Names|异常名称

由于异常应该是类,因此在这里应用类命名约定。但是,应该在异常名称上使用后缀“Error”(如果异常确实是错误)

代码语言:javascript
复制
class CustomError(Exception):
    pass

class FileReadError(IOError):
    pass
Global Variable Names|全局变量名称

希望这些变量仅在一个模块内部使用。约定与函数的约定几乎相同。

设计为通过 from M import * 使用的模块应该使用 __all__ 机制防止导出全局变量,或者使用在这些全局变量前加下划线的旧约定(这可能是为了表示这些全局变量是“模块非公开的”)。

代码语言:javascript
复制
_global_variable = 42  # 模块非公开的全局变量

__all__ = ['public_variable']  # 通过 from module import * 导入的公开变量列表

public_variable = 10  # 公开的全局变量
Function and Variable Names|函数和变量的命名

函数名应该是小写的,单词之间可以用下划线分隔以提高可读性。

变量名遵循与函数名相同的约定。

mixedCase 仅允许在已经是主流风格的上下文中使用(例如 threading.py),以保持向后兼容性。

代码语言:javascript
复制
# 推荐
def calculate_total(items):
    # 函数体
    pass

user_name = "John"
total_amount = 100.0

# 不推荐
def calculateTotal(items):
    # 避免使用 mixedCase 风格
    pass
Function and Method Arguments|函数和方法参数

始终将self用作实例方法的第一个参数。

始终将cls用作类方法的第一个参数。

如果函数参数的名称与保留关键字冲突,通常最好附加一个单个下划线,而不是使用缩写或拼写破坏。因此,class_clss更好。 (也许更好的方法是通过使用同义词来避免此类冲突。)

代码语言:javascript
复制
class MyClass:
    def __init__(self, x):
        self.x = x

    def instance_method(self):
        print(f"Instance method called. Value of x: {self.x}")

    @classmethod
    def class_method(cls):
        print("Class method called.")

# 创建类的实例
obj = MyClass(42)

# 调用实例方法
obj.instance_method()

# 调用类方法
MyClass.class_method()
Method Names and Instance Variables|方法名称和实例变量

应该使用函数命名规则,即小写字母,单词之间用下划线分隔以提高可读性

仅在非公共方法和实例变量中使用一个前导下划线。

为了避免与子类冲突,使用两个前导下划线来调用Python的名称混淆规则。

Python使用类名混淆这些名称:如果类Foo有一个名为__a的属性,就无法通过Foo.__a访问它(坚持的用户仍然可以通过调用Foo._Foo__a来获取访问权限)。通常,双前导下划线应仅用于避免与设计为被子类化的类中的属性发生名称冲突。

注意:对于使用__names存在一些争议。

代码语言:javascript
复制
class MyClass:
    def __init__(self):
        # 使用一个前导下划线表示这是一个非公共实例变量
        self._non_public_variable = 42

    def public_method(self):
        print("This is a public method.")

    def _non_public_method(self):
        # 使用一个前导下划线表示这是一个非公共方法
        print("This is a non-public method.")

class SubClass(MyClass):
    def __init__(self):
        super().__init__()
        # 使用两个前导下划线调用名称混淆规则,避免与父类的属性冲突
        self.__subclass_variable = "Subclass variable"

    def __subclass_method(self):
        # 使用两个前导下划线调用名称混淆规则,避免与父类的方法冲突
        print("This is a subclass method.")

# 创建类的实例
obj = MyClass()

# 访问公共方法和实例变量
obj.public_method()
print(obj._non_public_variable)

# 创建子类的实例
sub_obj = SubClass()

# 访问子类的方法和实例变量
sub_obj._SubClass__subclass_method()
print(sub_obj._SubClass__subclass_variable)
Constants|常量

常量通常在模块级别定义,并使用所有大写字母,用下划线分隔单词。示例包括 MAX_OVERFLOWTOTAL

Designing for Inheritance|为继承而设计

总是决定类的方法和实例变量(统称为“属性”)是公共的还是非公共的。如果有疑虑,选择非公共;将其公开比将公共属性变为非公共属性更容易。

公共属性是您希望类的不相关客户使用的属性,您承诺避免向后不兼容的更改。非公共属性不打算由第三方使用;您不保证非公共属性不会更改甚至被删除。

我们在这里不使用“私有”这个术语,因为在Python中没有真正的私有属性(除非做了一般不必要的工作)。

另一类属性是属于“子类 API”(在其他语言中通常称为“受保护”)的属性。某些类设计为可继承,可以通过继承或修改类的行为来使用这些属性。在设计这样的类时,请谨慎明确地决定哪些属性是公共的,哪些是子类 API 的一部分,哪些真正只能由基类使用。

代码语言:javascript
复制
class Person:
    def __init__(self, name, age):
        self._name = name  # 非公共属性
        self.__age = age   # 子类 API 属性

    def get_name(self):
        return self._name

    def set_name(self, name):
        self._name = name

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if age > 0:
            self.__age = age

# 使用示例
person = Person("Alice", 25)

# 公共方法和非公共属性的访问
print(person.get_name())  # Alice
person.set_name("Bob")
print(person.get_name())  # Bob

# 子类 API 属性的访问
print(person.get_age())  # 25
person.set_age(30)
print(person.get_age())  # 30

在这个例子中,_name 被设计为非公共属性,而 __age 被设计为子类 API 属性。使用相应的 gettersetter 方法来访问和修改这些属性,这样可以提供一定的控制和封装性。请注意,这里使用的是约定而不是强制性规则,Python 并不会强制属性的可见性。

在此基础上,以下是Python的准则:

  • 公共属性不应以下划线开头。
    • 如果公共属性名称与保留关键字冲突,请在属性名称后附加一个单下划线。这比使用缩写或拼写错误更可取。(然而,请注意,对于已知是类的变量或参数,特别是类方法的第一个参数,cls是首选的拼写。)
  • 对于简单的公共数据属性,最好只公开属性名称,而不是复杂的访问器/修改器方法。
    • 请记住,Python为将来的增强提供了一条简单的路径,如果您发现简单的数据属性需要增加功能性行为。在这种情况下,使用属性将功能实现隐藏在简单的数据属性访问语法之后。
    • 尝试使功能行为无副作用,尽管缓存等副作用通常是可以接受的。
    • 避免使用计算成本高昂的操作的属性;属性表示调用者相信访问是(相对)廉价的。
  • 如果您的类旨在被子类化,并且有一些不希望子类使用的属性,请考虑使用双下划线开头和无下划线结尾的名称。
    • 只使用简单类名作为混淆名称,因此如果子类选择相同的类名和属性名,仍然可能发生名称冲突。
    • 名称混淆可能会使某些用途,如调试和__getattr__(),变得不太方便。然而,名称混淆算法有很好的文档记录,可以手动执行。
    • 这会调用Python的名称混淆算法,其中类的名称被混淆到属性名称中。这有助于避免在子类中意外包含具有相同名称的属性时发生属性名称冲突。
    • 请注意:
    • 并非所有人都喜欢名称混淆。请努力平衡避免意外名称冲突的需求和高级调用者的潜在使用。
Public and Internal Interfaces| 公共和内部接口

向后兼容性保证仅适用于公共接口。因此,用户必须能够清晰地区分公共接口和内部接口。

公共接口的明确定义。

  • 已记录的接口被认为是公共的,除非文档明确声明它们是临时或内部接口,免除通常的向后兼容性保证。所有未记录的接口都应被视为内部接口。 def public_function(): """This is part of the public interface.""" ...

使用__all__属性明确声明公共API。

  • 为了更好地支持内省,模块应该使用__all__属性明确声明其公共API的名称。将__all__设置为空列表表示该模块没有公共API。 __all__ = ['public_function', 'public_class']

即使使用__all__,内部接口也应添加前导下划线。

  • 即使使用__all__属性,内部接口(包、模块、类、函数、属性等)仍应以单个前导下划线作为前缀。 _internal_function = ...

包含命名空间的内部性。

  • 如果包含命名空间(包、模块或类)被视为内部的,则其包含的接口也被视为内部的。

导入的名称应被视为实现细节。

  • 其他模块不得依赖对导入名称的间接访问,除非它们是包含模块 API 的明确文档部分,例如 os.path 或包的 __init__ 模块从子模块中暴露功能。

通过明确定义公共接口和内部接口,可以确保用户在使用公共功能时有稳定的体验,而内部实现的改变则不会影响用户的代码。这种清晰的接口设计有助于提高代码的可维护性和可用性。

Programming Recommendations|编程建议

代码应该以不影响 Python 的其他实现(PyPy、Jython、IronPython、Cython、Psyco等)的方式编写。

避免依赖于 CPython 特定的优化。

  • 不要依赖于 CPython 对于形式为 a += ba = a + b 的原地字符串连接的高效实现。这种优化在 CPython 中也很脆弱(仅适用于某些类型),在不使用引用计数的实现中根本不存在。在库的性能敏感部分,应该使用 ''.join() 形式,以确保在不同的实现中串联发生在线性时间内。

使用 isis not 进行与单例的比较。

  • 比较单例对象(如 None)时,应始终使用 isis not,而不是相等运算符。 # Good if some_variable is None: print("It's None") # Avoid if some_variable == None: print("It's None")

注意使用 if x 时确保意图。

  • 当测试默认为 None 的变量或参数是否被设置为其他值时,谨慎使用 if x,而更应该使用 if x is not None。其他值可能具有在布尔上下文中为假的类型(例如容器)。

使用 is not 操作符而不是 not ... is。虽然两者在功能上相同,但前者更易读,更受欢迎:

代码语言:javascript
复制
# 正确:
if foo is not None:
    
# 错误:
if not foo is None:

在实现具有丰富比较的排序操作时,最好实现所有六个操作(__eq____ne____lt____le____gt____ge__),而不是依赖其他代码仅执行特定的比较。为了最小化工作量,functools.total_ordering() 装饰器提供了一个工具,用于生成缺失的比较方法。

PEP 207 指出 Python 假定了反射性规则。因此,解释器可能将 y > xx < y 互换,y >= xx <= y 互换,并可能互换 x == yx != y 的参数。sort()min() 操作保证使用 < 运算符,而 max() 函数使用 > 运算符。

但最好实现所有六个操作,以防止在其他上下文中引起混淆。

代码语言:javascript
复制
from functools import total_ordering

@total_ordering
class MyClass:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

通过使用 @total_ordering 装饰器,你只需要定义 __eq____lt__ 方法,而其他的比较方法会被自动生成。这可以减少你实现的代码量,同时确保了正确的比较行为。

  • __eq__ (Equal): x == y
  • __ne__ (Not Equal): x != y
  • __lt__ (Less Than): x < y
  • __le__ (Less Than or Equal): x <= y
  • __gt__ (Greater Than): x > y
  • __ge__ (Greater Than or Equal): x >= y

始终使用 def 语句,而不是将 lambda 表达式直接绑定到标识符的赋值语句:

代码语言:javascript
复制
# 正确:
def f(x): return 2*x

# 错误:
f = lambda x: 2*x

第一种形式意味着生成的函数对象的名称具体为 'f',而不是通用的 ''。这对于回溯和字符串表示更有用。使用赋值语句消除了 lambda 表达式可以相对于显式 def 语句提供的唯一好处(即它可以嵌套在较大表达式中)的好处。

将异常派生自 Exception 而不是 BaseException。直接从 BaseException 继承应该保留给那些几乎总是不应该捕获的异常。 设计异常层次结构时,应基于捕获异常的代码可能需要的区别,而不是异常被引发的位置。力求通过程序方式回答“发生了什么问题?”这个问题,而不仅仅是陈述“发生了问题”(参见 PEP 3151,以了解内置异常层次结构的例子,学到了这个教训)。 类命名约定也适用于这里,尽管如果异常是错误,应该给异常类添加后缀 “Error”。用于非局部流程控制或其他形式的信号传递的非错误异常不需要特殊的后缀。

代码语言:javascript
复制
class CustomError(Exception):
    pass

class AnotherError(Exception):
    pass

CustomErrorAnotherError都是从Exception派生的,它们表示错误情况。这使得在处理异常时更容易理解异常的类型。

适当地使用异常链接raise X from Y 应该用于指示明确的替换,而不会丢失原始的回溯。 当有意替换内部异常(使用 raise X from None)时,请确保将相关细节传递给新的异常(例如,在将 KeyError 转换为 AttributeError 时保留属性名称,或者在新的异常消息中嵌入原始异常的文本)。

代码语言:javascript
复制
try:
    # some code that may raise an exception
    result = 10 / 0
except ZeroDivisionError as original_exception:
    # raise a new exception with additional context
    raise ValueError("Invalid calculation") from original_exception

在这个例子中,ValueError异常替代了ZeroDivisionError异常,并通过from original_exception保留了原始异常的回溯信息。

捕获异常时,尽量提及具体的异常,而不要使用裸的 except: 子句

代码语言:javascript
复制
try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

裸的 except: 子句会捕获 SystemExitKeyboardInterrupt 异常,使得使用 Control-C 更难中断程序,并且可能掩盖其他问题。如果你想捕获所有表示程序错误的异常,使用 except Exception:(裸的 except 等效于 except BaseException:)。

一个良好的经验法则是将裸的 except 子句的使用限制为两种情况:

代码语言:javascript
复制
# 不好的例子:裸的 except,捕获几乎所有异常,包括 SystemExit 和 KeyboardInterrupt
try:
    # some code that may raise an exception
    result = 10 / 0
except:
    # handle the exception
    print("An error occurred")

# 好的例子:捕获指定的异常,或者至少捕获 Exception
try:
    # some code that may raise an exception
    result = 10 / 0
except ZeroDivisionError:
    # handle the specific exception
    print("Cannot divide by zero")
except Exception as e:
    # handle other exceptions
    print(f"An error occurred: {e}")
  • 如果异常处理程序将打印或记录回溯信息;至少用户将意识到发生了错误。
  • 如果代码需要进行一些清理工作,但然后允许异常上升到上层使用 raise。在这种情况下,try...finally 可能是处理的更好方法。

在捕获操作系统错误时,更倾向于使用在 Python 3.3 中引入的显式异常层次结构,而不是检查 errno 值。

此外,对于所有的 try/except 子句,将 try 子句限制为绝对必要的最小代码量。这样可以避免掩盖错误:

代码语言:javascript
复制
# Correct:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

# Wrong:
try:
    # Too broad!
    return handle_value(collection[key])
except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)

当一个资源仅在代码的特定部分使用时,应使用 with 语句确保在使用后及时而可靠地进行清理。也可以使用 try/finally 语句。

代码语言:javascript
复制
# 使用 try/finally 语句管理资源
resource = acquire_resource()
try:
    # 在此处使用资源
    process_resource(resource)
finally:
    # 确保在任何情况下都会释放资源
    release_resource(resource)

当上下文管理器执行除获取和释放资源之外的操作时,应通过单独的函数或方法调用它们:

代码语言:javascript
复制
# Correct:
with conn.begin_transaction():
    do_stuff_in_transaction(conn)

# Wrong:
with conn:
    do_stuff_in_transaction(conn)

后一种示例没有提供任何信息,表明 __enter____exit__ 方法正在执行除在事务后关闭连接之外的其他操作。在这种情况下,明确是很重要的。

代码语言:javascript
复制
class DataProcessor:
    def __enter__(self):
        # 在这里执行获取资源的操作,例如打开文件
        self.file = open('example.txt', 'r')
        return self

    def process_data(self):
        # 在这里执行处理数据的操作
        data = self.file.read()
        print(data.upper())  # 举例:将文本转换为大写并打印出来

    def __exit__(self, exc_type, exc_value, traceback):
        # 在这里执行释放资源的操作,例如关闭文件
        self.file.close()

# 使用上下文管理器
with DataProcessor() as dp:
    dp.process_data()
# 文件已在 with 语句块结束时自动关闭

在这个例子中,DataProcessor 类是一个上下文管理器,负责处理文件的打开和关闭,以及数据的处理。__enter__ 方法用于获取资源,__exit__ 方法用于释放资源。process_data 方法用于处理数据。将数据处理的逻辑提取到 process_data 方法中,使得上下文管理器的职责清晰可见。

在 return 语句中保持一致性。一个函数中的所有 return 语句应该返回一个表达式,或者所有 return 语句都不应该返回任何值。如果任何 return 语句返回一个表达式,则所有不返回任何值的 return 语句应明确说明为 return None,并且如果可达,函数末尾应该有一个明确的 return 语句:

代码语言:javascript
复制
# Correct:
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)

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

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

使用 ''.startswith() ''.endswith()而不是字符串切片来检查前缀或后缀。 startswith()endswith() 更清晰,更不容易出错:

代码语言:javascript
复制
# Correct:
if foo.startswith('bar'):

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

对象类型的比较应该始终使用 isinstance() 而不是直接比较类型:

代码语言:javascript
复制
# Correct:
if isinstance(obj, int):

# Wrong:
if type(obj) is type(1):

对于序列(字符串、列表、元组),利用空序列为假的事实:

代码语言:javascript
复制
# Correct:
if not seq:
if seq:

# Wrong:
if len(seq):
if not len(seq):

不要编写依赖于显著尾随空白的字符串字面值。这种尾随空白在视觉上是无法区分的,而一些编辑器(或最近的reindent.py)将对它们进行修剪。

代码语言:javascript
复制
# 不建议的写法(依赖于字符串末尾的空白)
message = "This is a message with trailing space.    "

# 推荐的写法(去掉末尾的空白)
message = "This is a message without trailing space."

如果确实需要保留末尾空白,可以考虑使用特殊字符(如空格或制表符)进行表示,以避免在不同环境中的处理不一致。

不要使用 == 将布尔值与 TrueFalse 进行比较:

代码语言:javascript
复制
# Correct:
if greeting:

# Wrong:
if greeting == True:

# Wrong:
if greeting is True:

try...finally 结构的 finally 块内部使用 return/break/continue 这类控制流语句,如果这些语句会跳出 finally 块,是不鼓励的。这是因为这样的语句会隐式取消在 finally 块中传播的任何活动异常:

代码语言:javascript
复制
# Wrong:
def foo():
    try:
        1 / 0
    finally:
        return 42

在这个例子中,当除法操作引发 ZeroDivisionError 时,finally 块中的 return 语句会取消异常的传播,而且函数的返回值将是 42,而不是引发的异常。为了避免这种情况,最好在 finally 块中避免使用能够跳出块的控制流语句,以确保异常能够正确传播。如果需要在 finally 块中执行清理操作,可以考虑将清理逻辑放在 finally 块之外,或者使用其他方式来处理清理逻辑。

Variable Annotations|函数注释

PEP 526 引入了变量注释。对它们的样式建议与上述函数注释的建议相似:

模块级变量、类和实例变量以及局部变量的注释应在冒号后有一个空格。

在冒号之前不应有空格。

如果赋值右侧有内容,则等号两侧应该正好有一个空格:

代码语言:javascript
复制
# Correct:

code: int

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

# Wrong:

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

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

尽管 PEP 526 被接受用于 Python 3.6,但变量注释语法是所有 Python 版本的存根文件中首选的语法(有关详细信息,请参阅 PEP 484)。

References|参考

  • https://peps.python.org/pep-0008/
  • https://github.com/python/peps/blob/main/peps/pep-0008.rst
  • Barry’s GNU Mailman style guide
  • Donald Knuth’s The TeXBook, pages 195 and 196.
  • http://www.wikipedia.com/wiki/CamelCase
  • Typeshed repo
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI科技时讯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Python代码规范pep8
    • Introduction|简介
      • A Foolish Consistency is the Hobgoblin of Little Minds |愚蠢的一贯性是小心灵的小妖精
        • Code Lay-out|代码布局
          • Indentation|缩进
          • Tabs or Spaces|制表符还是空格
          • Maximum Line Length|最大代码行长度
          • Should a Line Break Before or After a Binary Operator|应该在二元运算符之前还是之后换行
          • Blank Lines|空白行
          • Source File Encoding|源文件编码
          • Import|引入
          • Module Level Dunder Names|模块级别的双下划线命名
        • String Quotes|字符串引号
          • One-line Doctrings| 一行文档字符串
          • Multi-line Docstrings|多行文档字符串
          • Handling Docstring Indentation|处理文档字符串缩进
        • Whitespace in Expressions and Statements|表达式和语句中的空格
          • Pet Peeves|令人讨厌的代码习惯
          • Other Recommendations|其他推荐
        • When to Use Trailing Commas|何时使用尾随逗号
          • Comments|注释
            • Block Comments|块注释
            • Inline Comments|内联注释
            • Documentation Strings|文档字符串
          • Naming Conventions|命名约定
            • Overriding Principle|压到一切的原则
            • Descriptive: Naming Styles|描述性:命名风格
            • Prescriptive: Naming Conventions|规定性:命名约定
            • Public and Internal Interfaces| 公共和内部接口
          • Programming Recommendations|编程建议
            • Variable Annotations|函数注释
        • References|参考
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档