python编程规范-google

https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/#lint

一.语法规范

1)对你的代码使用pylint;

2)仅对包和模块使用导入
使用 import x 来导入包和模块.
使用 from x import y , 其中x是包前缀, y是不带前缀的模块名.
使用 from x import y as z, 如果两个要导入的模块都叫做y或者y太长了.

3)使用模块的全路径名来导入每个模块
# Reference in code with complete name.
import sound.effects.echo
# Reference in code with just module name (preferred).
from sound.effects import echo

4)允许使用异常, 但必须小心
建议尽量少使用,因为会隐藏bug.

5)尽量避免全局变量
避免使用全局变量,可以使用类变量来替代。
当以下情况可以使用全局变量:
1.脚本的默认选项;
2.模块级常量;
3.有时候将全局变量来缓存值或者作为函数返回值;
4.如果需要,全局变量应该仅在模块内部使用,并通过模块级的公共函数来访问。

6)鼓励使用嵌套/本地/内部类或函数–强烈推荐!!!
定义:类和函数都可以定义在方法、函数或者类中。因为封闭区间中定义的变量对 嵌套函数 是只读的。

7)列表推荐List Comprehensions
1.Lambda使用

L = [i for i in range(10) if i % 2 != 1]
>>> L
[0, 2, 4, 6, 8]

2.yield使用
可以将yield初步看做‘return’,然后再将其看做是一个生成器‘generator‘,运行一次,generator会先得到yield 的结果,generator.next先输出这个,然后yield后面的程序输出的结果会放到再下一次的generator输出中。

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
>>> g.next()
starting...
4
>>> g.next()
('res:', None)
4
>>> g.next()
('res:', None)
4

>>> def foo(num):
...     print("starting...")
...     while num<10:
...         num=num+1
...         yield num
... 
>>> 
>>> for n in foo(0):
...     print(n)
... 
starting...
1
2
3
4
5
6
7
8
9
10

8)如果类型支持, 就使用默认迭代器和操作符. 比如列表, 字典及文件等.

9)按需使用生成器-yield, 鼓励使用。
所谓生成器函数, 就是每当它执行一次生成(yield)语句, 它就返回一个迭代器, 这个迭代器生成一个值. 生成值后, 生成器函数的运行状态将被挂起, 直到下一次生成.

10)lambda函数
适用于单行函数
与语句相反, lambda在一个表达式中定义匿名函数. 常用于为 map() 和 filter() 之类的高阶函数定义回调函数或者操作符.
1)lambda使用

g = lambda x,y:x*y
g(2,5)
>>> 10

2)lambda与filter/map/reduce一起使用。
lambda 为关键字。filter,map,reduce为内置函数。
lambda:实现python中单行最小函数。

g = lambda x: x * 2 
#相当于
def g(x):
    return x*2
  • filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)。
    filter(function or None, sequence) -> list, tuple, or string:入参为函数和列表/元组/字符串,返回值为item列表/元组/字符串。
  • map(function, sequence) :对sequence中的item依次执行function(item),将执行结果function(item)组成一个List返回。
    map(function, sequence[, sequence, …]) -> list:入参是为函数和列表/元组/字符串,返回值为function(item)列表。
  • reduce(function, sequence, starting_value):对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用。function接收的参数个数只能为2,先把sequence中第一个值和第二个值当参数传给function,再把function的返回值和第三个值当参数传给function,然后只返回一个结果。
    reduce(function, sequence[, initial]) -> value:入参是为函数和列表/元组/字符串和初始值,返回值为数值。

11)条件表达式
适用 单行函数
条件表达式是对于if语句的一种更为简短的句法规则. 例如:

>>> x = 1 if True else 2
>>> x
1

12)使用函数默认参数值
鼓励使用默认参数值,但是,不要在函数或方法定义中使用可变对象作为默认值.

13)属性(properties)
访问和设置数据成员时, 你通常会使用简单, 轻量级的访问和设置函数. 建议用属性(properties)来代替它们.只读属性应该用 @property 装饰器来创建.
通过消除简单的属性(attribute)访问时显式的get和set方法调用, 可读性提高了. 允许懒惰的计算. 用Pythonic的方式来维护类的接口. 就性能而言, 当直接访问变量是合理的, 添加访问方法就显得琐碎而无意义. 使用属性(properties)可以绕过这个问题. 将来也可以在不破坏接口的情况下将访问方法加上。

https://blog.csdn.net/weixin_42499361/article/details/82153712

class Student(object):
    def __init__(self, name,  score):
        self.name = name
        self.__score = score
        self.__grade = None

    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score
    @score.deleter
    def score(self):
        del self.__score

    @property
    def grade(self):
        if self.__score >= 80:
            self.__grade = 'A'
        if self.__score >= 60 and self.__score < 80:
            self.__grade = 'B'
        if self.__score < 60:
            self.__grade = 'C'
        return self.__grade

a = Student('ming', 100)
print(a.name)
print(a.score)
a.score = 60
print(a.grade)
del a.score

13)尽可能使用隐式false
Python在布尔上下文中会将某些值求值为false. 按简单的直觉来讲, 就是所有的”空”值都被认为是false. 因此0, None, [], {}, “” 都被认为是false.

  • 1)尽可能使用隐式的false,使用 if foo, 而不是 if foo != []:
  • 2)永远不要用 == 或 != 来比较单件,比如None,使用is 或者 is not。
  • 3)注意:当你写下 if x: 时,你其实想要表达的是 if x is not None。
    当你要测试一个默认值是None的变量或者参数是否被设置为其他值,这个值在布尔语义下可能是false。
  • 4)永远不要使用 == 将一个量与false进行比较,使用 if not x: 替代.如果你需要区分false 和None, 你应该这样使用:if not x and x is not None。
  • 5)对于序列(字符串,列表,元组),要注意空序列是false,因此,不要使用 if len(seq) 或者 if not len(seq),推荐使用 if not seq 或者if seq:。
  • 6)处理整数时,使用隐式false可能会得不偿失(即不小心将None当做0来处理),你可以将一个已知是整型(且不是len()的返回结果)的值与0比较。
  • 7)注意’0’(字符串)会被当做true。

14)尽可能使用字符串方法取代字符串模块.
使用函数调用语法取代apply().
使用列表推导, for循环取代filter(), map()以及reduce().

15)推荐使用 词法作用域(lexical scoping)
嵌套的Python函数可以引用外层函数中定义的变量, 但是不能够对它们赋值.
变量绑定的解析是使用词法作用域, 也就是基于静态的程序文本. 对一个块中的某个名称的任何赋值都会导致Python将对该名称的全部引用当做局部变量, 甚至是赋值前的处理. 如果碰到global声明, 该名称就会被视作全局变量.

16)函数与方法装饰器
如果好处很显然, 就明智而谨慎的使用装饰器
最常用的装饰器是:@classmethod 和 @staticmethod,用于将常规函数转换成类方法或静态方法.
用户也可以自己定义装饰器,例如,以下两段代码是等效的。

如果好处很显然, 就明智而谨慎的使用装饰器.
装饰器应该遵守和函数一样的导入和命名规则. 装饰器的python文档应该清晰的说明该函数是一个装饰器. 请为装饰器编写单元测试.
避免装饰器自身对外界的依赖(即不要依赖于文件, socket, 数据库连接等), 因为装饰器运行时这些资源可能不可用(由 pydoc 或其它工具导入).
应该保证一个用有效参数调用的装饰器在所有情况下都是成功的.

17)不要依赖内建类型的原子性.
优先使用Queue模块的 Queue 数据类型作为线程间的数据通信方式.
另外, 使用threading模块及其锁原语(locking primitives). 了解条件变量的合适使用方式, 这样你就可以使用 threading.Condition 来取代低级别的锁了.

18)避免使用威力过大的特性

二.风格规范

1)分号:不要在行尾加分号, 也不要用分号将两条命令放在同一行.

2)行长度:每行不超过80个字符

3)括号:宁缺毋滥的使用括号

4)缩进:用4个空格来缩进代码
绝对不要用tab, 也不要tab和空格混用.
对于行连接的情况, 你应该要么垂直对齐换行的元素(见 行长度 部分的示例), 或者使用4空格的悬挂式缩进(这时第一行不应该有参数):

5)空行:顶级定义之间空两行, 方法定义之间空一行
顶级定义之间空两行, 比如函数或者类定义.
方法定义, 类定义与第一个方法之间, 都应该空一行. 函数或方法中, 某些地方要是你觉得合适, 就空一行.

6)空格:按照标准的排版规范来使用标点两边的空格

7)’#‘ 井号键Shebang:只有包含main函数的文件才使用#!作为文件的开始.
大部分.py文件不必以#!作为文件的开始.
根据 PEP-394 , 程序的main文件应该以 #!/usr/bin/python2或者 #!/usr/bin/python3开始.

8)注释:

  • (1)对于一个文档字符串
    一个文档字符串应该这样组织:首先是一行以句号, 问号或惊叹号结尾的概述(或者该文档字符串单纯只有一行). 接着是一个空行. 接着是文档字符串剩下的部分, 它应该与文档字符串的第一行的第一个引号对齐.

  • (2)模块
    每个文件都应该包含一个许可样板。

  • (3)函数和方法
    一个函数必须要有文档字符串,除非它满足以下条件:

    • 1.外部不可见;
    • 2.非常短小;
    • 3.简单明了。
  • 文档字符串应该包含:函数做什么、输入和输出的详细描述。通常,不应该描述‘怎么做’。
    当别人编写代码调用此代码时,他不需要看一行代码,而是看文档字符串就知道怎么使用了。对于复杂的代码,在代码旁边加注释会比使用文档字符串更远意义。
    函数的文档字符串格式:

  • args:列出每个参数的名字,并在名字后面使用一个冒号和空格,分隔对该参数进行描述。如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致).

  • returns:(或yield)描述返回值的类型和语义,如果函数返回None,这部分可省略。

  • Raise: 列出与接口有关的所有异常。

(4)类的注释
类应该在其定义下有一个用于描述该类的文档字符串.
如果你的类有公共属性(Attributes), 那么文档中应该有一个属性(Attributes)段. 并且应该遵守和函数参数相同的格式.

(5)块注释和行注释
最需要写注释的是代码中那些技巧性的部分。
例如,如果你在下次 代码审查 的时候,必须解释一下,那么你应该现在就给它写注释。对于一些复杂的操作,应该在其操作开始前写上若干行注释。
对于不是一目了然的代码,应在其行尾添加注释。

为了提高可读性,注释应该至少离开代码2个空格。
另一方面,绝不要描述代码!!!

9)类:如果一个类不继承自其它类, 就显式的从object继承. 嵌套类也一样.

继承自 object 是为了使属性(properties)正常工作, 并且这样可以保护你的代码, 使其不受 PEP-3000 的一个特殊的潜在不兼容性影响. 这样做也定义了一些特殊的方法, 这些方法实现了对象的默认语义, 包括 new, init, delattr, getattribute, setattr, hash, repr, and str

10)字符串:即使参数都是字符串, 使用%操作符或者格式化方法格式化字符串. 不过也不能一概而论, 你需要在+和%之间好好判定.

避免在循环中用+和+=操作符来累积字符串。由于字符串是不可变的,这样做会创建不必要的临时对象,并导致二次方而不是线性的运行时间。推荐方案:可以将每个子串加入列表,然后在循环结束后用 .join 连接列表.(也可以将每个子串写入一个 cStringIO.StringIO缓存中。)

在同一文件中,保持使用字符串引号的一致性。
为多行字符串使用三重双引号"""而非三重单引号’’’。当且仅当项目中使用单引号’来引用字符串时, 才可能会使用三重’‘’为非文档字符串的多行字符串来标识引用.
文档字符串必须使用三重双引号”“”. 不过要注意, 通常用隐式行连接更清晰, 因为多行字符串与程序其他部分的缩进方式不一致.

11)文件和sockets:在文件和sockets结束时, 显式的关闭它.
推荐使用 “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.python.org/”)) as front_page:
for line in front_page:
print line

Legacy AppEngine 中Python 2.5的代码如使用”with”语句, 需要添加 “from future import with_statement”.

12)TODO注释:为临时代码使用TODO注释, 它是一种短期解决方案. 不算完美, 但够好了.
TODO注释应该在所有开头处包含”TODO”字符串, 紧跟着是用括号括起来的你的名字, email地址或其它标识符. 然后是一个可选的冒号. 接着必须有一行注释, 解释要做什么.
主要目的是为了有一个统一的TODO格式, 这样添加注释的人就可以搜索到(并可以按需提供更多细节). 写了TODO注释并不保证写的人会亲自解决问题. 当你写了一个TODO, 请注上你的名字.

# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.

如果你的TODO是”将来做某事”的形式, 那么请确保你包含了一个指定的日期(“2009年11月解决”)或者一个特定的事件(“等到所有的客户都可以处理XML请求就移除这些代码”).

13)import导入格式:每个导入应该独占一行

导入总应该放在文件顶部, 位于模块注释和文档字符串之后, 模块全局变量和常量之前.
导入应该按照从最通用到最不通用的顺序分组:
1.标准库导入
2.第三方库导入
3.应用程序指定导入

14)语句:通常每个语句应该独占一行
如果是if语句, 只有在没有else时才能这样做.
特别地, 绝不要对 try/except 这样做, 因为try和except不能放在同一行.

15)命名
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.
应该避免的名称

  • 单字符名称, 除了计数器和迭代器.
  • 包/模块名中的连字符(-)
  • 双下划线开头并结尾的名称(Python保留, 例如__init__)

命名约定

  • 所谓‘内部(Internal)’表示仅模块内可用,或者,在类内是保护或者私有的。
  • 用单下换线(_)开头表示模块变量是protected的(使用from module import *时不会包含)
  • 用双下划线(__)开头的实例变量或者方法表示类内私有
  • 将相关的类和顶级函数放在同一个模块中,不像Java,没必要限制一个类一个模块。
  • 对类名使用大写字母开头的单词,但是模块名称应该使用小写加下划线的方式,如lower_with_under.py

16)Main
即使是一个打算被用作脚本的文件, 也应该是可导入的. 并且简单的导入不应该导致这个脚本的主功能(main functionality)被执行, 这是一种副作用. 主功能应该放在一个main()函数中.
在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 if name == ‘main’ , 这样当模块被导入时主程序就不会被执行.

所有的顶级代码在模块导入时都会被执行. 要小心不要去调用函数, 创建对象, 或者执行那些不应该在使用pydoc时执行的操作.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值