Python后端编码规范

1首要:保持一致性原则

如果你只是改动一个大代码块的其中一小部分,那么请将你改动代码的编码风格与周围代码的编码风格保持一致

为了保持一致性,在下列情况下可以违背本规范:

  1. 遵循本规范会降低可读性
  2. 与周围其他代码不一致
  3. 代码在引入规范前完成,暂时没有理由修改

2代码布局

2.1缩进
  1. 每级缩进使用4个空格

  2. 括号中的续行应使用垂直隐式缩进或悬挂缩进

    • 使用垂直隐式缩进时,续行应该与被圆括号、方括号、花括号包裹起来的其他元素对齐
    • 使用悬挂缩进时,第一行不应该有参数,并使用缩进以区分自己时续行
    # (垂直隐式缩进) 与左括号对齐
    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)
    
  3. if语句条件跨行时,应添加额外的缩进

    YES:
    
    # 额外添加缩进
    if (this_is_one_thing
            and that_is_another_thing):
        do_something()
    NO:
    
    # 没有额外缩进,不推荐
    if (this_is_one_thing and
        that_is_another_thing):
        do_something()
    
  4. 连续行多于两行时,应使用悬挂缩进,将右括号换行并回退缩进

    # 右括号回退
    my_list = [
        1, 2, 3,
        4, 5, 6,
    ]
    result = some_function_that_takes_arguments(
        'a', 'b', 'c',
        'd', 'e', 'f',
    )
    
2.2续行
  1. 续行的首选方法是使用小括号、中括号、大括号,其次是反斜杠;如with语句中:

    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())
    
  2. 对于一般操作符,换行点应选择在操作符之后;对于运算符,换行点应在运算符之前;

    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)
    
    income = (gross_wages
              + taxable_interest
              + (dividends - qualified_dividends)
              - ira_deduction
              - student_loan_interest)
    
  3. 对于链式语法,应在.前使用反斜杠续行,续行使用悬挂缩进

    YES:
    
    # 悬挂缩进
    query = session.query(models.XXX) \
        .filter_by(xxxxxxxxxxx) \
        .first()
    # 全悬挂缩进
    its_a_long_long_query = session \
        .query(
    		models.Accountinfo.id,
            models.Accountinfo.realname,
            models.Accountinfo.sex) \
        .join(models.Customer.id==models.Accountinfo.id) \
        .filter_by(
            shop_id=shop_id,
            id=self.current_user.id) \
        .all()
    # “.” 换行悬挂缩进+括号中续行垂直隐式缩进
    its_a_long_long_query = session \
        .query(models.Accountinfo.id,
               models.Accountinfo.realname,
               models.Accountinfo.sex) \
        .join(models.Customer.id==models.Accountinfo.id) \
        .filter_by(shop_id=shop_id,
                   id=self.current_user.id) \
        .all()
        
    NO:
    
    query = session.query(models.Accountinfo.id,
                          models.Accountinfo.realname,
                          models.Accountinfo.sex) \
                      .join(models.Customer.id==models.Accountinfo.id) \
                      .filter_by(shop_id=shop_id,
                                 id=self.current_user.id) \
                      .all()
    its_a_long_long_query = session.query(models.XXX) \
                                   .filter_by(xxxxxxxxxxx) \
                                   .first()
    
  4. 每行尽量不要超过80个字符

2.3空行
  1. 使用两个空行分隔顶级函数定义、类定义
  2. 使用一个空行分隔类内的方法定义
  3. 额外的空行可以用来分隔几组相关的函数,但是要尽量减少使用
  4. 额外的空行可以在函数中用于分割不同的逻辑块,但是要尽量减少使用
2.4导入
  1. 一行导入语句中只导入一个库,库==模块?

    YES:
        
    import os
    import sys
    from subprocess import Popen, PIPE
    
    NO:
    
    import sys, os
    
  2. 全局导入始终在文件的顶部,在模块注释和文档字符串之后,在模块全局变量和常量之前;

  3. 导入顺序按照标准库、第三方库、本地库顺序导入,各组导入之间应加一个空行;

  4. 使用绝对路径导入,通常可读性更好;在绝对路径比较长的情况下,也可以使用相对路径;

  5. 当在某个包含类的模块中导入类时,这样的书写方式是合理的:

    from myclass import MyClass
    from foo.bar.yourclass import YourClass
    
    # 但如果这样的书写方式引起类名冲突,请这样书写
    
    import myclass
    import foo.bar.yourclass
    # 使用 myclass.MyClass 和 foo.bar.yourclass.YourClass 来对其进行引用 
    
  6. 禁止使用通配符导入

    通配符导入from <module> import *应该避免,因为不清楚命名空间有哪些名称存在,混淆读者和许多自动化的工具。

3字符串引用

  1. 优先使用双引号字符串,如果在字符串中已包含单引号或双引号,那么就用另一个,避免反斜杆的使用,以免发生转义;

  2. 对于文档字符串,始终使用三重双引号:

    def kos_root():
        """ Return the pathname of the KOS root directory """
        global _kos_root
        if _kos_root:
            return _kos_root
        ...
    
  3. 对于多行字符串,始终使用三重双引号而非三重单引号。

4空格的使用

  1. 括号里边避免空格;

    # YES
    
    spam(ham[1], {eggs: 2})
    
    # NO
    
    spam( ham[1], {eggs: 2} )
    
  2. 逗号、冒号、分号前不要加空格;

  3. 索引操作中的冒号当作操作符处理前后要有同样的空格;

  4. 函数的左括号前不要加空格;

  5. 序列的左括号前不要加空格;

  6. 操作符左右各加一个空格;

  7. 行末不要留多余的空格,空白行不要有任何空格;

  8. 二元运算符两边放置一个空格;

    涉及 =、符合操作符 ( += , -= 等) 、比较 ( == , < , > , != , <> , <= , >= , in , not in , is , is not )、布尔 ( and , or , not )

  9. 优先级较高的运算符或操作符的前后不建议有空格;

    # 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)
    
  10. 关键字参数和默认值参数的前后不要加空格;

    # YES
    
    def complex(real, image=0.0):
        return magic(r=real, i=image)
    
    # NO
    
    def complex(real, image = 0.0):
        return magic(r = real, i = image)
    
  11. 函数注释中,=前后要有空格,:->的前面无空格,后面有空格;

    # YES
    
    def munge(input: AnyStr):
    def munge(sep: AnyStr = None):
    def munge()-> AnyStr:
    def mubge(input: AnyStr, sep: AnyStr = None, limit=1000):
        
    # NO
    
    def munge(input: AnyStr=None):
    def munge(input:AnyStr):
    def munge(input: AnyStr)->PosInt:
    

5注释

5.1总体原则

与代码自相矛盾的注释比没注释更差,修改代码时要优先更新注释。

5.2块注释
  1. 块注释写在要注释的代码前,并和这些代码有同样的缩进;
  2. 块注释每行以#开头,#后如果有内容,#和内容之间必须留一个空格;
  3. 块注释内的段落用仅包含单个#的行分割。
5.3行内注释
  1. 慎用行内注释;

  2. 行内注释是指注释和语句在同一行,放在语句之后,至少用两个空格和语句分开;

  3. 避免无谓的注释。

    # YES
    
    x = x + 1  # Compensate for border
    
    # NO
    
    x = x + 1 # Increment x
    
5.4TODO注释
  1. 为所有的临时代码、占位代码编写TODO注释

  2. 代码审查过程中,遇到可能存在性能或逻辑问题的代码,但又不急于当时修改的,应编写TODO注释

  3. TODO注释应该在所有开头处包含TODO字符串,紧跟着是用括号括起来的名字、email地址或其它标识符,然后是一个可选的冒号,接着必须有一行注释,解释要做什么。

    # TODO(kl@gmail.com): Use a "*" here for string repetition.
    # TODO(Zeke) Change this to use relations.
    
5.5文档字符串
  1. 为所有公共模块、函数、类、方法书写文档字符串;非公开方法不一定有文档字符串,但应有注释(出现在def行之后)来描述这个方法用来做什么;

  2. 多行文档字符串,结尾的"""应该单独成行;

    """return a foobang
    optional plotz says to frobnicate the bizbaz first.
    """
    
  3. 单行的文档字符串,结尾的"""在同一行。

6命名规范

6.1首要原则

用户可见的API命名应遵循使用约定而不是实现

6.2总体原则
  1. 类名、方法名、变量名不要因为追求简短而使用晦涩的缩写;

  2. 模块命名尽量短小,使用全部小写的方式,可以使用下划线;

  3. 包命名尽量短小,使用全部小写的方式,不可以使用下划线;

  4. 类的命名使用 CapWords 的方式,模块内部使用的类采用 _CapWords 的方式;

  5. 异常命名使用 CapWords + Error 后缀的方式;

  6. 全局变量尽量只在模块内有效,前缀一个下划线;

  7. 函数命名使用全部小写的方式,可以使用下划线;

  8. 常量命名使用全部大写的方式,可以使用下划线;

  9. 类的属性(方法和变量)命名使用全部小写的方式,可以使用下划线;

  10. 类的属性有3中作用域 public、non-public 和 subclass API,可以理解成 C++ 中的 public、private、protected,non-public 属性前,前缀一条下划线,防止 import 时被导入;

  11. 类的属性若与关键字名字冲突,后缀一下划线,尽量不要使用缩略等其他方式;

  12. 为避免与子类属性命名冲突,在类的一些属性前,前缀两条下划线,会自动触发名字重组;比如:类Foo中声明__a,访问时,只能通过Foo._Foo__a,避免歧义;

  13. 实例方法第一个参数必须是self,类方式第一个参数必须是cls;

  14. 变量命名时,对于同一类的变量名称建议将区别项后置,例如:

    session
    session_readonly
    session_statistic 
    
6.3变量
  1. 常量:大写加下划线USER_CONSTANT
  2. 私有变量:小写和一个前缀下划线_private_value
  3. python中不存在私有变量这么一说,若是遇到需要保护的变量,使用小写和一个前置下划线;但这只是程序员之间的约定,用于警告说明这是一个私有变量,外部类不要去访问它;但实际上,外部类还是可以访问到这个变量。
  4. 内置变量:小写,两个前置下划线和两个后置下划线__class__
6.4函数和方法
  1. 私有方法: 小写和一个前导下划线 def _secrete(self):

    这里和私有变量一样,并不是真正的私有访问权限。同时也应该注意一般函数不要使用两个前导下划线(当遇到两个前导下划线时,Python 的名称改编特性将发挥作用)。特殊函数后面会提及;

  2. 特殊方法:小写和两个前导下划线,两个后置下划线 def __add__(self, other):

    这种风格只应用于特殊函数,比如操作符重载等;

  3. 函数参数:小写和下划线,缺省值等号两边无空格 def connect(self, user=None):

6.5类
  1. 类总是使用驼峰格式命名,即所有单词首字母大写其余字母小写。类名应该简明,精确,并足以从中理解类所完成的工作。常见的一个方法是使用表示其类型或者特性的后缀,例如:SQLEngine``MimeTypes
  2. 对于基类而言,可以使用一个 Base 前缀 BaseCookie

7编码建议

7.1比较和判空
  1. 尽可能使用isis not取代==,比如 if x is None 要优于 if x == None

  2. is not代替not ... is,前者的可读性更好

    # YES
    
    if foo is not None
    
    # NO
    
    if not foo is None
    
  3. 对序列(字符串、列表、元组)判空,有如下规则:

    # YES
    
    if not seq:
        pass
    
    if seq:
    
  4. 不要使用 == 进行布尔比较

    # YES
    
    if greeting:
        pass
    
    # NO
    
    if greeting == True:
        pass
    
  5. 使用 startswith() 和 endswith() 代替切片进行序列前缀或后缀的检查

    # YES
    
    if foo.startswith("bar"):
        pass
    
    # NO
    
    if foo[:3] == "bar":
    
  6. 使用 isinstance() 代替 type() 进行类型的比较

    # YES
    
    if isinstance(obj, int):
        pass
    
    # NO
    
    if type(obj) is type(1):
        pass
    
7.2字符串处理
  1. 合理选择 % 操作符或者格式化方法格式化字符串;

  2. 避免在循环中使用 + 和 += 操作符来累加字符串;

    由于字符串是不可变的,这样做会创建不必要的临时对象,并且导致二次方而不是线性的运行时间;可以将每个子字符串加入列表,然后在循环结束后用.join连接列表。

7.3其他
  1. 捕获异常时尽量指明具体异常,而不是空except:子句;

    空 “except:” 子句会捕捉 SystemExit 和 KeyboardInterrupt 异常,难以用 Control-C 中断程序,并可掩盖其他问题。如果 你捕捉信号错误之外所有的异常,使用 “except Exception”。

    空 “except:” 子句适用的情况两种情况:

    1. 打印出或记录了traceback,至少让用户将知道已发生错误
    2. 代码需要做一些清理工作,并用 raise转发了异常。这样try…finally可以捕捉到它
  2. 函数或者方法在没有返回时要明确返回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)
    
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值