Python编码规范

Python编码规范

1 排版

1.1 Indentation缩进

在参数过多时适当缩进

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)

换行应该使用同级的缩进

Yes:

foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

No:

foo = long_function_name(var_one, var_two,
    var_three, var_four)

Yes:

if (this_is_one_thing and
    that_is_another_thing):
    do_something()

注释需要具有相同的缩进

if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

以关系符开始应该增加缩进

if (this_is_one_thing
        and that_is_another_thing):
    do_something()

变量声明中应该有相同的缩进

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

1.2 最大行长

所有行最大长度被限制在79个字符

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

过长需要续行使用’\’二元操作符的位置

No: 操作符里操作数过远

income = (gross_wages +
          taxable_interest +
         (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

Yes: 正确的方式可以更容易的去匹配操作符与操作数

income = (gross_wages
        + taxable_interest
        + (dividends - qualified_dividends)
        - ira_deduction
        - student_loan_interest)

1.3空行

  • 顶层函数和类定义:前后两行空白
  • 类方法定义:前后一行空白
  • 应该添加额外空行来曲分不同组的函数
  • 在函数定义内部使用空行来分隔不同的逻辑片段
  • 源文件编码:

Python2使用ASCII编码或者Python3使用UTF-8编码,不应该声明编码类型。
所以在使用Python2时,我们应该在源文件开始声明编码类型。
#coding=utf-8

1.4 Import引入

  • 引入模块应该分行

Yes:

import os       
import sys

No:

import sys, os

It’s okay to say this though:

from subprocess import Popen, PIPE

引入声明应该总是在文件顶部,仅在任何模块的注释和文档字符后面,同时也要处在全局变量和常量声明之前。

引入应该按照下面的顺序分组并书写

  1. 标准库
  2. 相关第三方库
  3. 本地 应用/库 特殊量的引入

你应该用空行隔开每个分组

• 在用户包/或者可引用的文件夹外的文档内import 模块应该使用绝对引用的方式

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

• 在用户包内import 模块应该使用相对引用的方式,确保包内路径的可靠性

from . import sibling

从当前目录下引用

from .. import x

从当前目录上层文件夹引用

from .sibling import example

从当前目录下sibling层引用

标准库应当总是绝对引用

应当尽量避免使用下面的引入方式:

from <module> import *

1.4 行内注释

  • 应该保守的使用行内注释
  • 行内注释应该至少与代码间隔两个空格
  • 应避免无意义的注释

如下

x = x + 1                 # Increment x

这样会更有意义:

x = x + 1                 # Compensate for border

2. 命名要求

2.1 最重要的规则

作为为其他用户开放的公共变量的名字必须遵循管理,而且能够显示它的用途

2.2 命名形式

有很多不同的命名方式,下面的命名方式都是可以使用

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (or CapWords, or CamelCase — so named because of the bumpy look of its letters ). This is also sometimes known as StudlyCaps.

Note: 当使用驼峰是命名的时候,应当将命名中的缩写全部用大写表示. 因此 HTTPServerError 要比HttpServerError更好。
mixedCase (不同于CapitalizedWords在首字母的大小写上),

Capitalized_Words_With_Underscores (这是个很丑的方式)

  • 也可以使用一个独特简短的前缀为一组相关的变量进行命这在Python里使用的并不多。
  • 还需要补充说明的一些规则

_single_leading_underscore : 弱的内部变量标示. 当使用语句
from M import *语句引入模块时并不会引用以单个下划线开头的变量。

single_trailing_underscore_ : 使用后单个下划线来避免变量名与python关键词的冲突,如:class_class

__double_leading_underscore : 当命名一个类属性是,以双下划线开头, 启用命名矫正功能(在类FooBar内, __boo变成了_FooBar__boo)。

double_leading_and_trailing_underscore : 以双下划綫开头和结尾Python中可以重载的一些魔法方法。

E.g.__init__ , __import__ or __file__ . 不要去创造相似的名字。

3. 指定的: 命名规则

3.1 需要避免的命名

  • 永远不要使用字母’l’ (小写的字母L), ‘O’ (大写字母的o), 或者 ‘I’ (大写字母的i) 作为单个字母的变量名。

3.2 包和模块名

  • 模块应该用短的,而且全是小写字母的名字。

  • 使用下划线分割,当使用下划线可以提高模块命名的可读性时。

  • Python包应该也使用短的,而且全小写的名字,并不推荐使用下划线分割。

  • 当存在一个使用C或C++编写的模块,同时包含一个Python模块为其提供高层封装时, C/C++ 模块应该用前下划线开始(e.g. _socket )。

3.3 类命名

  • 类名通常应该使用首字母大写的命名规则(CapWords convention)。

  • 函数命名规则可以被使用当接口被记录在文档中,同时主要用于随时可以调用的函数
    The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.

  • 需要注意的是,对于内建函数的命名,这里与一个不同的规则,他们一般是单独的一个单词,又是是两个单词。

  • CapWords convention,仅仅用于异常名和内建的常量

Note that there is a separate convention for builtin names: most builtin names are single words (or two words run together), with the CapWords convention used only for exception names and builtin constants.

3.4 类型变量名

  • 类型变量名通常应该使用CapWords,且更倾向于使用简短的名字,如: T、 AnyStr、 Num等。相应的,建议添加后缀_co或者 _contra给那些被用来声明covariant或者contravariant 行为的变量。
    例子:from typing import TypeVar

      VT_co = TypeVar('VT_co', covariant=True)
      KT_contra = TypeVar('KT_contra'contravariant=True)
    

3.5 异常名

因为异常是一个类, 按照已经提供的类命名方法来命名。你还需要在你的异常名后面添加”Error”后缀(如果它真的是一个错误).

3.6 全局变量命名

  • (假设这些变量仅仅在一个模块内部使用) 它们的命名规则和函数是一样的。

  • 被设计成为使用from M import *语句来引入的模块,应该使用__all__机制 来防止引入全局量, 或者使用一个下划线为前缀的旧命名规则为全局量命名(这样就指明了这些全局量是非公开的)。

3.7 函数命名

函数名应该是小写,有必要为了提高可阅读性可以使用下划线将单词分开。

mixedCase被允许使用,仅仅是为了延续上文中使用的经典命名方式(e.g. threading.py)来维持向前的兼容性

3.8 函数和方法参数

  • 总是使用self作为实例方法的第一个参数。

  • 总是使用cls作为类方法的第一个参数。

  • 如果一个函数的参数的名字与保留字冲突,为其添加一个后下划线要比缩写或者污染这个变量名更好。因此class_要比clss好。 (可能更好的方法是使用同义词来避免这样的冲突。)

3.9 方法名和实例变量

  • 使用函数命名规则: 有必要小写那些使用下划线分隔的单词,如果能够提高可读性。

  • 使用单个前下划线来为非公开的方法和实例变量命名。

  • 为了避免和子类的命名冲突,使用两个前下划线来启用Python的命名矫正功能。

    Python用类型名来矫正这些名字。如果类型Foo有一个属性,命名为__a,使用Foo.__a并不能调用它。(坚持的用户仍然可以使用Foo._Foo__a来调用。) 通常双下划线应该是用于被设计成可继承的类,来避免类属性与子类属性的命名冲突。

注意: 对于__names的使用时存在一些争议的(见下文)。

3.10 常量

  • 常量一般定义于模块层级,使用用下划线分隔的全大写字母来命名。

    Examples include MAX_OVERFLOW and TOTAL .

3.11 继承的设计

  • 总是决定一个方法或者变量(均被称为属性)应该是公开还是不公开的。如果存在疑问就选择非公开。那么在未来将其变成公开的会比变成非公开的更简单。

  • 公开的属性是那些你希望开放给不相关的客户端来使用的属性,你的恪守承诺可以让这些不相关的使用者,消除向下不兼容的变化。非公开的属性是指,不会被第三方调用的属性。你不为非公开的属性提供不变化或者不被移除的保证。

    我们不使用“private”这里,是因为在Python里没有真正私有的变量(在不付出大量非必要的工作量下)。

  • 另一种范畴的属性是那些“subclass API”(通常被声明为“protected”在其他语言中)。有些类是设计成为被继承的,不是去扩展就是修改这些类的行为。
    当设计这样的类时,要仔细的去做出明确的决定,哪些属性是公开的,哪些属性是子类接口,哪些属性是真的只被你的基类使用。

有了这些考虑,下面是一些python化的指引:

  • 公开的属性应该没有前下划线。

  • 如果你的公开属性名与保留字冲突,在其名字尾部添加单下划线。(但是, 尽管有这条规则,对于已知是一个类的任何变量或者参数,特别是类方法的第一个参数,cls都是一个更好的拼写)

  • 对于简单的公开的数据属性,最好仅仅暴露属性名,而不要存取器方法。记住Python对未来的增强提供简单的方式。如果你发现一个简单的数据需要去扩展函数的行为,在这种情况下,使用内置的“property”函数来隐藏简单数据在读写或者删除时,产生的其他行为。

Note 1: Property只工作于新式的类。

Note 2: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.

Note 3: 在计算密集型的操作中避免使用property的方法。

  • 如果你的类是被设计成为被继承的,同时,你有那些你不希望子类去使用的属性,那么考虑使用前双下划线(不带后下划线)去命名它们。这触发了Python的命名矫正算法,这时,类名会被加入属性名中。这对避免属性名冲突,当子类并不是故意的包含哪些相同的名字。

Note 1: 注意只有简单的类命名被用于矫正命名。所以当有一个子类选择相同的类名和相同的属性名,你还是会有命名冲突。

Note 2: 命名矫正较不方便的用在确定的用途上,比如调试或者获得属性(getattr()方法),尽管命名矫正算法已经有了很好的文档,也可以简单的手动实施。

Note 3: 并不是每个人都喜欢命名矫正,试着去平衡的使用它来降低高级调用者潜在的使用中出现意外的命名冲突的可能性。

4. 公开和内部接口

  • 只是对公开接口,保证向前的兼容性,所以使用者能够清楚的判断公开接口和内部接口是很重要的。
  • 写入文档的接口被认为是公开的,除非文档明确说明,这些接口是临时得或者内部的,从而不在向前兼容的保证中。所有没有被写入文档的接口应该是内部的。
  • 为了更好的支持内省,模块应该明确的声明那些公开的API的名字,使用模块的__all__属性。设置__all__属性为一个空的列表,说明这个模块没有任何公开的API。
  • __all__的适当设定一样,内部接口(包、模块、类、函数、属性或者其他名字)应该仍然使用前单下划线作为前缀。
  • 一个接口被同样认为是内部的,当任何包含的命名空间(包、模块或者类)被认为是内部的。

5. 编程建议

5.1 基本建议

  • 代码应该以不会不利于其他实现的Python(PyPy、Jython、IronPython、Cython,、Psyco等)的方式。

    例如,不要依赖CPython对字符串有效率的连接方法,如a+=b或者a=a+b的表述。这个优化在CPython(只对几种类型生效)中是脆弱的,同时,这个方式不会被实现,当解释器不使用refcounting。对于库中性能敏感的部分,应该是''.join()形式。这保证在不同的Python实现中,连接字符串操作是线性时间的。

5.2 比较操作

  • 与单个变量的比较,比如None,应该总是用is或者is not来操作,永远不应该是等号操作符。

  • 同样的,小心书写if x当你的真实意图是if x is not None

    例如,当测试一个变量或者参数默认是None,是否被设置成为其他值是,这个值,可能是一个类型(例如容器),可以在布尔层面是False。a=[],bool(a)False

  • 使用is not操作符而不是not…is。虽然两个表述都是正确的,但是前者更易读也更推荐

    Yes:

      if foo is not None:
    

    No:

      if not foo is None:
    
  • 当用rich comparisons实现排序操作,最好实现全部留个比较操作符(eq , ne ,lt , le , gt , ge)而不是依赖其他代码只演习了一个特定的比较
    为了最小化我们的工作量,functools.total_ordering()装饰器提供了一个生成缺失比较方法的工具。
    PEP207指出自反性规则是被Python假设存在的。因此解释器可能会用x<y交换y>x,用y>=x交换x<=y,也可能交换x==y和x!=y的参数。sort()和min()操作保证使用<操作符,max()函数使用>操作符。尽管如此,最好还是实施全部留个操作符,那么混淆将不会在其他地方产生。

  • 总是使用def声明,而不是一个赋值声明,将一个变量直接绑定到一个lambda表达式。

    Yes:

      def f(x): return 2*x
    

    No:

      f = lambda x: 2*x
    

    第一种形式意味着结果函数对象的名字是f而不是一般的’<lambda>’,这在栈追踪中会更有用,同时显示它的名字。赋值声明的使用消除唯一一个lambda表达式的好处,就是可以一个提供越过def声明(它可以被嵌入在一个大的表达式中)。

5.3 异常处理

  • 从Exception派生异常,而不是BaseException。直接从BaseException继承,是留给那些总是被捕捉为一个错误的东西的异常。

    设计基于代码捕捉异常的特性的异常分层是需要的,而不是找到异常抛出的地方。旨在以编程方式回答“什么错了”的问题,而不是只是声明”出现了一个错误”。类命名规则被使用在这里,尽管你需要为你的异常类添加Error后缀,如果这个异常是一个错误。没有错误的异常是被用在非本地的流控制或者其他形式的不需要特殊后缀的信号。

  • 适当的使用异常链。在Python3中,raise X from Y应该被用于明确的指出替换,而不丢失原始的栈追踪信息。

    当故意的替换一个内部异常(在Python2中使用raise X,或者在Python3.3+中使用raise X from None),需要保证相关的细节被转移到了新的异常中。(如,保留属性名,当转化KeyError为AttributeError,或者在新异常中嵌入原始异常的说明)

  • 当在Python2中触发一个异常时,使用raise ValueError('message')来替代老式的用法raise ValueError,'message'
    后者在Python3中不合法。

  • 在捕获异常时,尽可能的提出特定的异常,而不是裸的except: clause.因为很有可能会捕捉到别的异常,如键盘中断异常。

  • 当绑定一个捕捉到的异常到一个名字,尽量使用在Python2.6中增加的明确的命名绑定语法

      try:
          process_data()
          except Exception as exc:
          raise DataProcessingFailedError(str(exc))
    

    这是唯一在Python3中支持的语法,同时消除了老式逗号语法的混淆。

  • 当捕获操作系统错误时,最好是使用Python3.3中引入的明确的异常层次,而不是errno的值。

  • 额外的说明,对于全部的try/except语句,限制try中的语句的数量到一个尽量少的量,这可以避免掩盖bugs

    Yes:

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

    No:

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

5.4 with语句

  • 当一个资源对于一个特定部分的代码是一个内部变量时,使用with声明去保证,它会被使用后迅速、可靠的清理。一个try/finally声明也是可以接受的。

  • 上下文管理器应该被通过不同的函数或者方法调用,无论它们做除了获取和释放资源的任务,例如:

    Yes:

      with conn.begin_transaction():
          do_stuff_in_transaction(conn)
    

    No:

      with conn:
          do_stuff_in_transaction(conn)
    

    后一个例子并没有提供任何信息去指明,enterexit方法在做什么除了在传输后关闭连接,在这种情况下,明确是很重要的。

5.5 return语句

  • 对return声明一致。一个函数中全部return语句要么都返回一个表达式,要么任何一个都不。如果任何一个返回语句返回一个表达式,任何那些没有返回值的返回语句,应该显示的返回None,一个显示的返回声明,应该在函数的结尾保留(如果可以到达)。

    Yes:

      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)
    

    No:

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

5.6 字符串操作

  • 使用字符串方法而不是string模块
    字符串方法一般要快的多,同时和unicode字符串使用相同的API。如果需要想前兼容比Python2.0更老的版本,需要重写这些规则。

  • 使用.startswith().endswith()而不是字符串切分,来检查前缀或者后缀。
    .startswith().endswith() 更干净的,而且更不容易出错的。

    For example:

    Yes: if foo.startswith('bar'):

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

5.7 类型比较

  • 对象类型比较应该总是使用isinstance() 而不是直接比较它们们的类型。

    Yes: if isinstance(obj, int):

    No: if type(obj) is type(1):

  • 当检查一个对象是不是一个字符串。请留意,他可能是也是一个unicode字符串,在Python2中,str和unicode有相同的基类,basestring,所以你可以这样做:

    if isinstance(obj, basestring):

注意在Python3中unicode和basestring都不存在了(只有str类型),同时一个字节对象也不再是一种类型的字符串了(它是一个整型序列了)。

  • 对于序列,(字符串,列表,元组),使用空序列就是False的事实。

    Yes:

      if not seq:
      if seq:
    

    No:
    if len(seq):
    if not len(seq):

  • 不要写字符串字面值依赖于显著的空格结尾,这些尾部的空格是视觉上不能区别的,同时有些编辑器(更近点的, reindent.py)会修剪它们

  • 不要比较布尔值与True或者False使用==

    Yes: if greeting:

    No: if greeting == True:

    Worse: if greeting is True:

    原文作者:帝Bug
    原文地址: https://www.jianshu.com/p/bc34a9750dc0
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞