zoukankan      html  css  js  c++  java
  • Python

    此编码风格指南主要基于 Google Python Style Guide [中译版],结合百度python使用习惯和实际开发情况制定。

    1. 语言规范

    1.1 import

    [强制]  禁止使用from xxx import yyy语法直接导入类或函数(即yyy只能是module或package,不能是类或函数)。

    [强制]  禁止使用from xxx import *

    [强制]  import时必须使用package全路径名(相对PYTHONPATH),禁止使用相对路径(相对当前路径)。

    1.2 异常

    [强制]  除非重新抛出异常,禁止使用except:捕获所有异常。

    [强制]  捕捉异常时,应当使用as语法,禁止使用逗号语法。

    [强制]  禁止使用双参数形式(raise MyException, 'Error Message')或字符串形式(raise 'Error Message')语法抛异常。

    # 正确
    try:
        ……
    except Exception as e:
        raise MyException('Error Message!')
    
    # 错误
        raise MyException, 'Error Message'raise 'Error Message'

    [强制]  如果需要自定义异常,应该在模块内定义名为Error的异常基类。该基类必须继承自Exception。其它异常类都从该Error类派生而来。

    class Error(Exception):
        def __init__(self,err='XXX错误'):
            Exception.__init__(self,err)
    
    class DatabaseException(Error):
        def __init__(self,err='数据库错误'):
            DatabaseException.__init__(self,err)

    ====

    [建议] 可以使用异常。但使用前请务必详细了解异常的行为,谨慎使用。

    [建议] 除非重新抛出异常,否则不建议捕获ExceptionStandardError。如果捕获,必须在日志中记录所捕获异常信息。

    [建议] 建议try中的代码尽可能少。避免catch住未预期的异常,掩藏掉真正的错误。

    [建议] 建议使用finally子句来执行那些无论try块中有没有异常都应该被执行的代码,这对于清理资源常常很有用。例如:文件关闭。

    1.3 全局变量

    [强制]  禁止使用全局变量。除了以下例外:

    • 脚本默认参数
    • 模块级常量

    [强制]  如果定义全局变量,必须写在文件头部。

    1.4 构造函数

    [建议]  类构造函数应该尽量简单,不能包含可能失败或过于复杂的操作。

    1.5 函数返回值

    ·[强制]  函数返回值必须小于等于3个

                3个以上时必须通过class/namedtuple/dict等具名形式进行包装。

    ## Ex1
    def get_numbers():
        return 1, 2, 3
    
    a, b, c = get_numbers()
    
    ## Ex2 class
    class Person(object):
        def __init__(self, name, gender, age, weight):
            self.name = name
            self.gender = gender
            self.age = age
            self.weight = weight
    
    ## Ex3 namedtuple
    import collections
    Person = collections.namedtuple('Person', 'name gender age weight')
    
    ## Ex4 dict
    def get_person_info():
        return Person('jjp', 'MALE', 30, 130)
    
    person = get_person_info()

    1.6 嵌套/局部/内部类或函数

    [建议]  不推荐使用嵌套/局部/内部类或函数。

    1.7 列表推导

    [强制]  可以使用列表推导。mapping、loop、filter部分单独成行,且最多只能写一行。禁止多层loop或filter。

    (解释:复杂的列表推导难以理解,建议转换成对应的for循环)

    1.8 默认迭代器和操作符

    ·[强制] 对容器或文件的只读遍历,应该使用内置的迭代方法,不要使用返回list的方式遍历。

    ·[强制] [PY013] 对容器类型,使用innot in判断元素是否存在。而不是has_key

    # ============ YES ============
    for key in adict:
        ....
    if key not in adict: 
        ....
    if obj in alist:
        ....
    for line in afile:
        ....
    for k, v in dict.iteritems():
        ....
    
    # 删除毕业学生
    for id in students.keys():
        if students[id].graduated:
            del students[id]
    
    # ============ No ============
    for key in adict.keys(): 
        ....
    if not adict.has_key(key): 
        ....
    for line in afile.readlines(): 
        ....
    
    #删除毕业学生
    for id in students:
        if students[id].graduated:
            del students[id]      # 抛出RuntimeError异常

    1.9 生成器

    ·[建议] 当返回较长列表数据时建议使用yield和generator函数。

    #返回n以内的奇数
    def odds(n):
        for i in xrange(1, n + 1):
            if i % 2 == 1:
                yield i
    
    for i in odds(1000):
        print i
    
    # ============================
    
    def odds(n):
        ret = []
        for i in xrange(1, n + 1):
            if i % 2 == 1:
                ret.append(i)
        return ret
    
    for i in odds(1000):
        print i

    1.10 lambda函数 

    ·[强制]  可以使用lambda函数,但仅限一行之内。

    1.11 条件表达式

    [强制]  条件表达式仅用于一行之内,禁止嵌套使用

    1.12 默认参数

    [强制]  仅可使用以下基本类型字面常量或常量作为默认参数:整数、bool、浮点、字符串、None

     (解释:以可修改的对象(如list、dict、object等)作为默认参数,可能会被不小心改掉,导致默认值发生变化,产生难以追查的错误)

    def foo(a, b=None):
        if b is None:
            b = []

    1.13 属性(properties)

    property() 函数的作用是在新式类中返回属性值。

    [强制]  可以使用property。但禁止在派生类里改写property实现。

    (解释:由于property是在基类中定义的,默认绑定到基类的实现函数。

                  若允许在派生类中改写property实现,则需要在基类中通过间接方式调用property实现函数。

                  这个方法技巧性太强,可读性差,所以禁止使用。)

    import math
    
    class Square(object):
        """ [A square with two properties: a writable area and a read-only perimeter.]
    
        To use:
        >>> sq = Square(3)
        >>> sq.area
        9
        >>> sq.perimeter
        12
        >>> sq.area = 16
        >>> sq.side
        4
        >>> sq.perimeter
        16
        """
    
        def __init__(self, side):
            self.side = side
    
        def __get_area(self):
            '''Calculates the 'area' property.'''
            return self.side ** 2
    
        def __set_area(self, area):
            '''Sets the 'area' property.'''
            self.side = math.sqrt(area)
    
        def __del_area(self, area):
            '''Del the 'area' property.'''
            del self.side
    
        area = property(__get_area, __set_area,
                        doc="""Gets or sets the area of the square.""")
    
        @property
        def perimeter(self):
            return self.side * 4

    1.14 True/False求值

    [强制]  禁止使用==!=判断表达式是否为None,应该用isis not None

    [强制]  当明确expr为bool类型时,禁止使用==!=True/False比较。应该替换为exprnot expr

    [强制]  判断某个整数表达式expr是否为零时,禁止使用not expr,应该使用expr == 0


    [建议]  建议显式转换到bool类型,慎用到bool类型的隐式转换。如使用隐式转换,你需要确保充分了解其语义

    if users is None or len(users) == 0:
        print 'no users'
    
    foo = True if not foo: self.handle_zero() if i % 10 == 0: self.handle_multiple_of_ten()

     2. 风格规范

    2.1 分号

    [强制]  禁止以分号结束语句

    [强制]  一行只能写一条语句,没有例外情况

    2.2 行列长度

    [强制]  每行不得超过100个字符

    [强制]  函数长度不得超过100行

    2.3 缩进

    ·[强制] 使用4个空格缩进,禁止使用tab缩进。

    ·[强制] 把单行内容拆成多行写时,要么与首行保持对齐;要么首行留空,从第二行起统一缩进4个空格;为与后面的代码区分,可以使用8空格缩进。

    (解释:不同编辑器对TAB的设定可能不同,使用TAB容易造成在一些编辑器下代码混乱,所以建议一率转换成空格。)

    # Aligned with opening delimiter
    foo = long_function_name(var_one, var_two,
                             var_three, var_four)
    
    # 4-space hanging indent in a dictionary
    foo = {
        long_dictionary_key:
            long_dictionary_value,
        ...
    }

    2.4 空行

    [强制]  文件级定义(类或全局函数)之间隔两个空行,类方法之间隔一个空行

    2.5 空格

    [强制]  圆括号、方括号、花括号内侧都不加空格

    spam(ham[1], {eggs: 2}, [])

    [强制]  参数列表, 索引或切片的左括号前不应加空格

    dict['key'] = list[index]

    [强制]  逗号、分号、冒号前不加空格,后边加一个空格

    if x == 4:
        print x, y
    x, y = y, x

    [强制]  所有二元运算符前后各加一个空格

    x == 1

    [强制]  关键字参数或参数默认值里的等号前后不加空格

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

    2.6 注释

    [强制]  使用文档字符串(docstring)描述module、function、class和method接口。docstring必须用三个双引号括起来。

    [强制]  对外接口部分必须用docstring描述,内部接口视情况自行决定是否写docstring。

    [强制]  接口的docstring描述至少包括功能简介、参数、返回值。如果可能抛出异常,必须注明。

    [强制]  每个文件都必须有文件声明,文件声明必须包括以下信息:版权声明,功能和用途简介,修改人及联系方式。

    """[parse error message]
    
        Args:
            e (error): [error]
        Returns:
            str: [parsed error message]
        Raises:
            IOError: An error occurred accessing the bigtable.Table object.
    """

    2.7 import格式

    [强制]  每行只能导入一个库

    [强制]  必须按如下顺序排列 

    import

    ,每部分之间留一个空行
    • 标准库
    • 第三方库
    • 应用程序自有库
    import os
    import sys
    
    from third.party import lib
    from third.party import foobar as fb
    
    import my.own.module

    2.8 命名规则

    [强制]  类(包括异常)名使用首字母大写驼峰式命名

    [强制]  常量使用全大写字母,单词间用下划线分隔

    [强制]  其它情况(目录/文件/package/module/function/method/variable/parameter)一律使用全小写字母,单词间用下划线分隔

    [强制]  protected成员使用单下划线前缀,private成员使用双下划线前缀

    [强制]  禁止使用双下划线开头,双下划线结尾的名字(类似__init__)

    3. 编程实践

    3.1 类继承

    [强制]  如果一个类没有基类,必须继承自object类。

    class SampleClass(object):
        pass
    
    class OuterClass(object):
        class InnerClass(object):
            pass
    
    class ChildClass(ParentClass):
        """Explicitly inherits from another class already."""

    3.2 字符串格式化与拼接

    [强制]  除了a+b这种最简单的情况外,应该使用%format格式化字符串。

    [强制] 不要使用+=拼接字符串列表,应该使用join

         x = '%s, %s!' % (imperative, expletive)
         x = '{}, {}!'.format(imperative, expletive)
         x = 'name: %s; score: %d' % (name, n)
         x = 'name: {}; score: {}'.format(name, n)
         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)

    3.3 文件和socket

    [强制]  用完文件或socket后必须显式关闭句柄。建议使用with语法简化开发

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

    3.4 主程序

    [强制]  所有module都必须可导入。如需要执行主程序,必须检查__name__ == '__main__'

    3.5 单元测试

    [建议]  推荐使用PyUnit做单元测试。是否需要做单元测试以及目标单测覆盖率由项目负责人自行决定。

    [建议]  推荐测试代码放在单独的test目录中。如果被测试代码文件名为xxx.py,那么测试代码文件应该被命名为xxx_test.py#

    # math_test.py
    # !/usr/bin/env python # -*- coding: gb18030 -*- import unittest import math class MathTestCase(unittest.TestCase): def test_sqrt(self): self.assertEqual(math.sqrt(4) * math.sqrt(4), 4) if __name__ == "__main__": unittest.main()

    3.4 日志输出

    [建议]  推荐使用python自带的logging库打印日志。

    [建议]  推荐默认日志格式:"%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d %(thread)d %(message)s", 时间格式:"%Y-%m-%d %H:%M:%S"

    [建议]  推荐线上程序使用两个日志文件:一个专门记录warning/error/critical日志,另一个记录所有日志。

    # log.py
    import os
    import logging
    import logging.handlers
    
    def init_log(log_path, level=logging.INFO, when="D", backup=7,
                 format="%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d * %(thread)d %(message)s",
                 datefmt="%m-%d %H:%M:%S"):
        """
        init_log - initialize log module
    
        Args:
          log_path      - Log file path prefix.
                          Log data will go to two files: log_path.log and log_path.log.wf
                          Any non-exist parent directories will be created automatically
          level         - msg above the level will be displayed
                          DEBUG < INFO < WARNING < ERROR < CRITICAL
                          the default value is logging.INFO
          when          - how to split the log file by time interval
                          'S' : Seconds
                          'M' : Minutes
                          'H' : Hours
                          'D' : Days
                          'W' : Week day
                          default value: 'D'
          format        - format of the log
                          default format:
                          %(levelname)s: %(asctime)s: %(filename)s:%(lineno)d * %(thread)d %(message)s
                          INFO: 12-09 18:02:42: log.py:40 * 139814749787872 HELLO WORLD
          backup        - how many backup file to keep
                          default value: 7
    
        Raises:
            OSError: fail to create log directories
            IOError: fail to open log file
        """
        formatter = logging.Formatter(format, datefmt)
        logger = logging.getLogger()
        logger.setLevel(level)
    
        dir = os.path.dirname(log_path)
        if not os.path.isdir(dir):
            os.makedirs(dir)
    
        handler = logging.handlers.TimedRotatingFileHandler(log_path + ".log",
                                                            when=when,
                                                            backupCount=backup)
        handler.setLevel(level)
        handler.setFormatter(formatter)
        logger.addHandler(handler)
    
        handler = logging.handlers.TimedRotatingFileHandler(log_path + ".log.wf",
                                                            when=when,
                                                            backupCount=backup)
        handler.setLevel(logging.WARNING)
        handler.setFormatter(formatter)
        logger.addHandler(handler)
  • 相关阅读:
    JMXRemote Source Downloads? Circular? 规格严格
    Java Heap Dump3 规格严格
    Java Heap Dump2 规格严格
    Java 时区总结 规格严格
    Java HeapDump 规格严格
    JDK 动态代理 规格严格
    常用日语100句
    Wine 1.0发布日期已定!
    细节的感动
    Blog正式改名
  • 原文地址:https://www.cnblogs.com/blitheG/p/14652330.html
Copyright © 2011-2022 走看看