- 编程规范
1.1. 命名规范
1.1.1. [强制] 命名不能以下划线或美元符号开始和结尾
反例: name / __name / $Object / name / name$ / Object$
1.1.2. [强制] 命名严禁使用拼音与英文混合的方式
反例: DaZhePromotion [打折] / getPingfenByName() [评分] / int 变量 = 3;
正例: ct / ct108 / uc / tcy / youku / hangzhou 等国际通用的名称,可视为英文。
1.1.3. [强制] 类名使用 UpperCamelCase 风格,必须遵从驼峰形式
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
1.1.4. [强制] 方法名、参数名、成员变量、局部变量都统一使用小写
正例: localvalue / gethttpmessage() / inputuserid
反例: localValue / getHttpMessage() / inputUserId
1.1.5. [强制] 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长
正例: MAX_STOCK_COUNT
1.1.6. [强制] 异常类命名使用 Error 结尾
正例: class InvalidValueError(Exception)
1.1.7. [强制] 单元测试文件必须以test_开头
正例: test_dclog.py
1.1.8. [强制] 单元测试方法名必须以test_开头,紧跟需要单元测试的方法的methodname
正例:
需要单元测试的方法
def hello(name):
pass
单元测试方法
def test_hello():
pass
1.1.9. [强制] 稀有方法/变量,采用__xxx的命名方式命名
正例:
__num = 10 # 私有变量__num赋值为10
def __hello(name): pass # 有个稀有方法叫__hello
1.1.10. [强制] 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词,包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式
正例: 应用工具类包名为 ct.dc.util、类名为 MessageUtils
1.1.11. [强制] 杜绝完全不规范的缩写,避免望文不知义
1.1.12. [推荐] 如果使用到了设计模式,建议在类名中体现出具体模式
将设计模式体现在名字中,有利于阅读者快速理解架构设计思想
正例:
class OrderFactory;
class LoginProxy;
class ResourceObserver;
1.1.13. [强制] 使用字典作为枚举使用,必须形成双向检索
正例:
import logging
NAME_LEVEL = {
'CRITICAL': logging.CRITICAL,
'ERROR': logging.ERROR,
'WARN': logging.WARNING,
'WARNING': logging.WARNING,
'INFO': logging.INFO,
'DEBUG': logging.DEBUG,
'NOTSET': logging.NOTSET,
logging.CRITICAL: 'CRITICAL',
logging.ERROR: 'ERROR',
logging.WARNING: 'WARNING',
logging.INFO: 'INFO',
logging.DEBUG: 'DEBUG',
logging.NOTSET: 'NOTSET'
}
1.1.14. [强制] 方法命名规约
获取单个对象的方法用 get 做前缀
获取多个对象的方法用 list 做前缀
获取统计值的方法用 count 做前缀
插入的方法用 save(推荐)或 insert 做前缀
删除的方法用 remove(推荐)或 delete 做前缀
修改的方法用 update 做前缀
1.2. 常量定义
1.2.1. [强制] 不允许出现任何魔法值
1.2.2. [强制] 不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护
1.3. 格式规范
1.3.1. [强制] 冒号之后必须换行
正例:
if num = 1:
return True
反例:
if num = 1: return True
1.3.2. [强制] 方法参数中默认值设置=,等于号左右不得留空格
正例:
def hello(name=None)
反例:
def hello(name = None)
1.3.3. [强制] ,逗号右侧需要留有一个空格
正例:
def hello(aaa=1, bbb=2)
array = [1, 2, 3, 4]
反例:
def hello(aaa=1,bbb=2)
array = [1,2,3,4]
1.3.4. [强制] 单行字符数限制不超过 120 个
1.3.5. [推荐] 没有必要增加若干空格来使某一行的字符与上一行的相应字符对齐
正例:
aaa = 3
bbbb = 4
ccccc = 5
反例:
aaa = 3
bbbb = 4
ccccc = 5
1.3.6. [强制] IDE 的 text file encoding 设置为 UTF-8
1.3.7. [强制] IDE 中文件的换行符使用 Unix 格式,不要使用 windows 格式
1.3.8. [推荐] 方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行
1.3.9. [强制] 单行的字符串用”或”“, 多行的字符串标注用”””
1.4. OOP 规约
1.4.1. [强制] 禁止通过一个类的对象引用访问此类的静态变量或静态方法
1.4.2. [强制] None的判断必须用is
正例:
if obj is None
反例:
if obj == None
1.4.3. [强制] 构造方法里面禁止加入任何业务逻辑
如果有初始化逻辑,请放在 init 方法中
1.4.4. [推荐] 当一个类有多个构造或者同名方法,这些方法应该按顺序放置在一起
便于阅读
1.4.5. [推荐] 类内方法定义顺序规范
公有方法或保护方法 > 私有方法
1.4.6. [强制]
PEP8 Python 编码规范
一 代码编排
1 缩进。4个空格的缩进(编辑器都可以完成此功能),不要使用Tap,更不能混合使用Tap和空格。
2 每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。
3 类和top-level函数定义之间空两行;类中的方法定义之间空一行;函数内逻辑无关段落之间空一行;其他地方尽量不要再空行。
二 文档编排
1 模块内容的顺序:模块说明和docstring—import—globals&constants—其他定义。其中import部分,又按标准、三方和自己编写顺序依次排放,之间空一行。
2 不要在一句import中多个库,比如import os, sys不推荐。
3 如果采用from XX import XX引用库,可以省略‘module.’,都是可能出现命名冲突,这时就要采用import XX。
三 空格的使用
总体原则,避免不必要的空格。
1 各种右括号前不要加空格。
2 逗号、冒号、分号前不要加空格。
3 函数的左括号前不要加空格。如Func(1)。
4 序列的左括号前不要加空格。如list[2]。
5 操作符左右各加一个空格,不要为了对齐增加空格。
6 函数默认参数使用的赋值符左右省略空格。
7 不要将多句语句写在同一行,尽管使用‘;’允许。
8 if/for/while语句中,即使执行语句只有一句,也必须另起一行。
四 注释
总体原则,错误的注释不如没有注释。所以当一段代码发生变化时,第一件事就是要修改注释!
注释必须使用英文,最好是完整的句子,首字母大写,句后要有结束符,结束符后跟两个空格,开始下一句。如果是短语,可以省略结束符。
1 块注释,在一段代码前增加的注释。在‘#’后加一空格。段落之间以只有‘#’的行间隔。
2 行注释,在一句代码后加注释。比如:x = x + 1 # Increment x
但是这种方式尽量少使用。
3 避免无谓的注释。
五 文档描述
1 为所有的共有模块、函数、类、方法写docstrings;非共有的没有必要,但是可以写注释(在def的下一行)。
2 如果docstring要换行,参考如下例子,详见PEP 257
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
六 命名规范
总体原则,新编代码必须按下面命名风格进行,现有库的编码尽量保持风格。
1 尽量单独使用小写字母‘l’,大写字母‘O’等容易混淆的字母。
2 模块命名尽量短小,使用全部小写的方式,可以使用下划线。
3 包命名尽量短小,使用全部小写的方式,不可以使用下划线。
4 类的命名使用CapWords的方式,模块内部使用的类采用_CapWords的方式。
5 异常命名使用CapWords+Error后缀的方式。
6 全局变量尽量只在模块内有效,类似C语言中的static。实现方法有两种,一是__all__机制;二是前缀一个下划线。
7 函数命名使用全部小写的方式,可以使用下划线。
8 常量命名使用全部大写的方式,可以使用下划线。
9 类的属性(方法和变量)命名使用全部小写的方式,可以使用下划线。
9 类的属性有3种作用域public、non-public和subclass API,可以理解成C++中的public、private、protected,non-public属性前,前缀一条下划线。
11 类的属性若与关键字名字冲突,后缀一下划线,尽量不要使用缩略等其他方式。
12 为避免与子类属性命名冲突,在类的一些属性前,前缀两条下划线。比如:类Foo中声明__a,访问时,只能通过Foo._Foo__a,避免歧义。如果子类也叫Foo,那就无能为力了。
13 类的方法第一个参数必须是self,而静态方法第一个参数必须是cls。
七 编码建议
1 编码中考虑到其他python实现的效率等问题,比如运算符‘+’在CPython(Python)中效率很高,都是Jython中却非常低,所以应该采用.join()的方式。
2 尽可能使用‘is’‘is not’取代‘==’,比如if x is not None 要优于if x。
3 使用基于类的异常,每个模块或包都有自己的异常类,此异常类继承自Exception。
4 异常中不要使用裸露的except,except后跟具体的exceptions。
5 异常中try的代码尽可能少。比如:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
要优于
try:
Too broad!
return handle_value(collection[key])
except KeyError:
Will also catch KeyError raised by handle_value()
return key_not_found(key)
6 使用startswith() and endswith()代替切片进行序列前缀或后缀的检查。比如
Yes: if foo.startswith(‘bar’):优于
No: if foo[:3] == ‘bar’:
7 使用isinstance()比较对象的类型。比如
Yes: if isinstance(obj, int): 优于
No: if type(obj) is type(1):
8 判断序列空或不空,有如下规则
Yes: if not seq:
if seq:
优于
No: if len(seq)
if not len(seq)
9 字符串不要以空格收尾。
10 二进制数据判断使用 if boolvalue的方式。
以上就是在pytho开发过程中的一些注意的点!
Python编码规范
阅读目录
1.2.1. 本规范的层次结构
1.2.2. 条目的级别和编号
python编码规范
- 前言
1.1. 一般信息[重要必读]
1.2. 如何使用本编程规范
1.2.1. 本规范的层次结构
1.2.2. 条目的级别和编号
1.3. The Zen of Python - 语言规范
2.1. import
2.2. 异常
2.3. 全局变量
2.4. 构造函数
2.5. 函数返回值
2.6. 嵌套/局部/内部类或函数
2.7. 列表推导
2.8. 默认迭代器和操作符
2.9. 生成器
2.10. lambda函数
2.11. 条件表达式
2.12. 默认参数
2.13. 属性(properties)
2.14. True/False求值
2.15. 函数和方法装饰器 - 风格规范
3.1. 分号
3.2. 行列长度
3.3. 括号
3.4. 缩进
3.5. 空行
3.6. 空格
3.7. 注释
3.8. import格式
3.9. 命名规则 - 编程实践
4.1. python解释器
4.2. 文件编码
4.3. 类继承
4.4. 字符串格式化与拼接
4.5. 文件和socket
4.6. 主程序
4.7. 单元测试
4.8. 日志输出 - 工具支持
展示eagle支持规则 - 前言
1.1. 一般信息[重要必读]
此编码风格指南主要基于 Google Python Style Guide [中译版],结合百度python使用习惯和实际开发情况制定。
这份文档存在的意义是让大家写出统一风格的代码,让百度的模块可维护性和可读性更好;
文档内容可能会与您的喜好冲突, 请尽量用包容的心态来接受; 不合理之处, 请反馈给py-styleguide@baidu.com
1.2. 如何使用本编程规范
回到顶部
1.2.1. 本规范的层次结构
本规范可分为三大部分,分别对Python语法、风格、编程实践作出规定与建议。
每一部分有若干专题,每一专题下有若干条目。
条目是规范的基本组成部分,每一条目由规定、定义、解释、示例、参考等项组成。
回到顶部
1.2.2. 条目的级别和编号
本规范的条目分两个级别
·[强制]:要求所有程序必须遵守,不得违反
·[建议]:建议遵守,除非确有特殊情况
1.3. The Zen of Python
建议每位python开发人员细读”python之禅”,理解规范背后的思想
"""
The Zen of Python
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!
-- by Tim Peters
"""
- 语言规范
2.1. import
·[强制] 禁止使用from xxx import yyy语法直接导入类或函数(即yyy只能是module或package,不能是类或函数)。
·[强制] [PY002] 禁止使用from xxx import *
·[强制] [PY003] import时必须使用package全路径名(相对PYTHONPATH),禁止使用相对路径(相对当前路径)。
2.2. 异常
·[建议] 可以使用异常。但使用前请务必详细了解异常的行为,谨慎使用
·[强制] [PY004] 禁止使用双参数形式(raise MyException, 'Error Message')或字符串形式(raise 'Error Message')语法抛异常。
·[强制] 如果需要自定义异常,应该在模块内定义名为Error的异常基类。该基类必须继承自Exception。其它异常类都从该Error类派生而来。
·[强制] 除非重新抛出异常,禁止使用except:捕获所有异常。
·[建议] 除非重新抛出异常,否则不建议捕获Exception或StandardError。如果捕获,必须在日志中记录所捕获异常信息。
·[强制] [PY007] 捕捉异常时,应当使用as语法,禁止使用逗号语法。
·[建议] 建议try中的代码尽可能少。避免catch住未预期的异常,掩藏掉真正的错误。
·[建议] 建议使用finally子句来执行那些无论try块中有没有异常都应该被执行的代码,这对于清理资源常常很有用。例如:文件关闭。
解释
raise MyException,'Error Message'或raise 'Error Message'语法将废弃
用户经常需要以统一的方式处理某模块抛出的所有异常,如果模块开发者自定义的异常没有统一基类,用户处理起来就很麻烦
except:或except Exception:或except StandardError:会捕获类似语法错误、Ctrl+C中断等底层异常。而一般情况下这不是用户代码要处理的
逗号式异常捕获语法将废弃
示例
YES:
raise MyException
raise MyException('Error Message')
class Error(Exception):
pass
try:
raise Error
except Error as error:
pass
NO:
raise 'Error Message'
raise MyException, 'Error Message'
try:
raise Error
except Error, error:
pass
2.3. 全局变量
·[强制] 禁止使用全局变量。除了以下例外:
脚本默认参数
模块级常量
·[强制] 如果定义全局变量,必须写在文件头部。
解释
全局变量破坏程序封装性,使代码维护困难
2.4. 构造函数
·[建议] 类构造函数应该尽量简单,不能包含可能失败或过于复杂的操作
解释
复杂的、可能出异常的构造函数通常语义不明确,与调用者的期望不符。且难以维护,容易出错。一般建议把复杂部分拆分到独立的函数中。
什么情况算”过于”复杂,由代码owner和reviewer根据经验自行判定
2.5. 函数返回值
·[强制] [PY004] 函数返回值必须小于等于3个。3个以上时必须通过class/namedtuple/dict等具名形式进行包装
解释
虽然python支持以tuple的形式返回多个值,但返回值过多时需要用户记住返回值顺序,使接口难以使用,容易用错
示例
YES:
def get_numbers():
return 1, 2, 3
a, b, c = get_numbers()
class Person(object):
def init(self, name, gender, age, weight):
self.name = name
self.gender = gender
self.age = age
self.weight = weight
or
import collections
Person = collections.namedtuple('Person', 'name gender age weight')
def get_person_info():
return Person('jjp', 'MALE', 30, 130)
person = get_person_info()
NO:
def get_numbers():
return 1, 2, 3, 4
a, b, c, d = get_numbers()
def get_person_info():
return 'jjp', 'MALE', 30, 130
name, gender, age, weight = get_person_info()
2.6. 嵌套/局部/内部类或函数
·[建议] 不推荐使用嵌套/局部/内部类或函数
解释
这些机制增加了代码理解难度,考虑到公司的平均python水平,不推荐使用
2.7. 列表推导
·[强制] [PY011] 可以使用列表推导。mapping、loop、filter部分单独成行,且最多只能写一行。禁止多层loop或filter。
解释
复杂的列表推导难以理解,建议转换成对应的for循环
示例
YES:
result = []
for x in range(10):
for y in range(5):
if x * y > 10:
result.append((x, y))
for x in xrange(5):
for y in xrange(5):
if x != y:
for z in xrange(5):
if y != z:
yield (x, y, z)
return ((x, complicated_transform(x))
for x in long_generator_function(parameter)
if x is not None)
squares = [x * x for x in range(10)]
eat(jelly_bean for jelly_bean in jelly_beans
if jelly_bean.color == 'black')
NO:
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
return ((x, y, z)
for x in xrange(5)
for y in xrange(5)
if x != y
for z in xrange(5)
if y != z)
2.8. 默认迭代器和操作符
·[强制] 对容器或文件的只读遍历,应该使用内置的迭代方法,不要使用返回list的方式遍历。
·[强制] [PY013] 对容器类型,使用in或not in判断元素是否存在。而不是has_key。
解释
可读性和性能更好。例如:
file.readlines()会一次性读入文件中所有行,存储到一个list中返回。当文件很大时,性能开销较大
当文件或容器较小时,调用返回list的函数也可以接受。请reviewer视具体情况决定是否通过。
如果遍历过程中需要对容器进行增删,请使用返回list的方式遍历
示例
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异常
2.9. 生成器
·[建议] 当返回较长列表数据时建议使用yield和generator函数。
解释
生成器函数可以避免返回大列表,占用过多内存、影响性能。同时还可以保持代码的优雅易读。
示例
YES:
返回n以内的奇数
def odds(n):
for i in xrange(1, n + 1):
if i % 2 == 1:
yield i
for i in odds(1000):
print i
NO:
返回n以内的奇数
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
2.10. lambda函数
·[强制] [PY014] 可以使用lambda函数,但仅限一行之内。
解释
lambda函数可以简化开发。但复杂的lambda函数可读性很差,应该避免
2.11. 条件表达式
·[强制] 条件表达式仅用于一行之内,禁止嵌套使用
解释
语法略小众,其它语言背景开发者(如C++)看起来比较困惑。写复杂了难以阅读维护
示例
YES:
单行简单条件表达式
x = true_value if cond else false_value
改写成if形式
if cond:
x = true_value
else
x = false_value
复杂if表达式
if t < 60:
unit = "seconds"
elif t < 3600:
unit = "minutes"
else
unit = "hours"
NO:
嵌套条件表达式
unit = "seconds" if t < 60 else "minutes" if t < 3600 else "hours"
2.12. 默认参数
·[强制] [PY016] 仅可使用以下基本类型字面常量或常量作为默认参数:整数、bool、浮点、字符串、None
解释
以可修改的对象(如list、dict、object等)作为默认参数,可能会被不小心改掉,导致默认值发生变化,产生难以追查的错误
默认值在module加载时求值,而不是每次调用时求值。复杂的默认值可能和预期不符合(例如下边例子中的time.time()和FLAGS.my_things )
示例
YES:
def foo(a, b=None):
if b is None:
b = []
No: def foo(a, b=[]):
...
No: def foo(a, b=time.time()): # The time the module was loaded???
...
No: def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed...
...
2.13. 属性(properties)
·[强制] 可以使用property。但禁止在派生类里改写property实现。
解释
由于property是在基类中定义的,默认绑定到基类的实现函数。若允许在派生类中改写property实现,则需要在基类中通过间接方式调用property实现函数。这个方法技巧性太强,可读性差,所以禁止使用。
示例
YES:
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)
area = property(__get_area, __set_area,
doc="""Gets or sets the area of the square.""")
@property
def perimeter(self):
return self.side * 4
NO:
class MySquare(Square):
def __get_area(self):
return math.pi * self.side ** 2 # overwrite doesn't work
def __set_area(self, area):
self.side = math.sqrt(area / math.pi) # overwrite doesn't work
2.14. True/False求值
·[建议] 建议显式转换到bool类型,慎用到bool类型的隐式转换。如使用隐式转换,你需要确保充分了解其语义
·[强制] [PY018] 禁止使用==或!=判断表达式是否为None,应该用is或is not None
·[强制] 当明确expr为bool类型时,禁止使用==或!=与True/False比较。应该替换为expr或not expr
·[强制] 判断某个整数表达式expr是否为零时,禁止使用not expr,应该使用expr == 0
解释
python中None、空字符串、0、空tuple、list、dict都会被隐式转换为False,这可能和用户预期的行为不一致。
为便于不太熟悉python语言的其它语言开发者理解python代码,建议”显式”表明到bool类型的转换语义
运算符==或!=的结果取决于__eq__函数,可能出现obj is not None,但obj==None的情况
if expr != False 当expr为None时,会通过检测。一般这不是用户期望的行为
from PEP:
Yes: if greeting:
No: if greeting == True:
Worse: if greeting is True:
当判断expr是否为0时,若expr为None,not expr也会返回True。一般这不是用户期望的行为
示例
YES:
if users is None or len(users) == 0:
print 'no users'
if foo == 0:
self.handle_zero()
if i % 10 == 0:
self.handle_multiple_of_ten()
Cautious:
if not users:
print "no users"
NO:
if len(users) == 0:
print 'no users'
if foo is not None and not foo:
self.handle_zero()
if not i % 10:
self.handle_multiple_of_ten()
2.15. 函数和方法装饰器
·[建议] 建议仅在以下几种情况下使用函数方法装饰器。其它情况下如有明显好处,且不影响代码可维护性,可以谨慎使用
@property、@classmethod、@staticmethod
自动化测试
第三方库要求使用的装饰器
解释
decorator太灵活,可以任意改变函数参数和返回值,容易产生非预期行为。滥用容易使代码可维护性性变差
decorator在import时执行,decorator代码执行中的错误很难处理和恢复
- 风格规范
3.1. 分号
·[强制] [PY021] 禁止以分号结束语句
·[强制] [PY022] 一行只能写一条语句,没有例外情况
3.2. 行列长度
·[强制] [PY023] 每行不得超过100个字符
·[强制] [PY024] 函数长度不得超过100行
解释
现在宽屏比较流行,所以从传统的80个字符限制扩展到100个字符
函数太长一般说明函数定义不明确,程序结构划分不合理,不利于维护
示例
字符串太长时可以分成两个字符串,python会自动join相临的字符串
x = ('This will build a very long long '
'long long long long long long string')
3.3. 括号
·[建议] 除非用于明确算术表达式优先级、tuple或者隐式行连接,否则尽量避免冗余的括号。
解释
python倾向于直观、简洁的代码
示例
YES:
if foo:
bar()
while x:
x = bar()
if x and y:
bar()
if not x:
bar()
return foo
for (x, y) in dict.iteritems(): ...
NO:
if (x):
bar()
if not(x):
bar()
return (foo)
3.4. 缩进
·[强制] 使用4个空格缩进,禁止使用tab缩进。
·[强制] 把单行内容拆成多行写时,要么与首行保持对齐;要么首行留空,从第二行起统一缩进4个空格;为与后面的代码区分,可以使用8空格缩进。
解释
不同编辑器对TAB的设定可能不同,使用TAB容易造成在一些编辑器下代码混乱,所以建议一率转换成空格。
在vim下,建议打开如下设置:
:set tabstop=4 设定tab宽度为4个字符
:set shiftwidth=4 设定自动缩进为4个字符
:set expandtab 用space自动替代tab
示例
YES:
Aligned with opening delimiter
foo = long_function_name(var_one, var_two,
var_three, var_four)
Aligned with opening delimiter in a dictionary
foo = {
long_dictionary_key: value1 +
value2,
...
}
4-space hanging indent; nothing on first line
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,
...
}
NO:
Stuff on first line forbidden
foo = long_function_name(var_one, var_two,
var_three, var_four)
2-space hanging indent forbidden
foo = long_function_name(
var_one, var_two, var_three,
var_four)
No hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
3.5. 空行
·[强制] [PY027] 文件级定义(类或全局函数)之间隔两个空行,类方法之间隔一个空行
3.6. 空格
·[强制] [PY028] 圆括号、方括号、花括号内侧都不加空格
Yes: spam(ham[1], {eggs: 2}, [])
No: spam( ham[ 1 ], { eggs: 2 }, [ ] )
·[强制] [PY029] 参数列表, 索引或切片的左括号前不应加空格
Yes: spam(1)
Yes: dict['key'] = list[index]
No: spam (1)
No: dict ['key'] = list [index]
·[强制] [PY030] 逗号、分号、冒号前不加空格,后边加一个空格
Yes:
if x == 4:
print x, y
x, y = y, x
No:
if x == 4 :
print x , y
x , y = y , x
·[强制] [PY031] 所有二元运算符前后各加一个空格
Yes: x == 1
No: x<1
·[强制] [PY032] 关键字参数或参数默认值里的等号前后不加空格
Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)
No: def complex(real, imag = 0.0): return magic(r = real, i = imag)
3.7. 注释
·[强制] [PY033] 使用docstring描述module、function、class和method接口。docstring必须用三个双引号括起来。
·[强制] 对外接口部分必须用docstring描述,内部接口视情况自行决定是否写docstring。
·[强制][PY034] 接口的docstring描述至少包括功能简介、参数、返回值。如果可能抛出异常,必须注明。
·[强制] 每个文件都必须有文件声明,文件声明必须包括以下信息:版权声明,功能和用途简介,修改人及联系方式。
·[建议] TODO注释格式必须为:
TODO: 干什么事情$负责人(邮箱前缀)$最终期限(YYYY-MM-DD)$
定义
模块、类、函数的第一个逻辑行的字符串称为文档字符串(docstring)。
解释
docstring可以通过help查看,可以通过pydoc自动提取文档。编写docstring是个非常好的习惯。
示例
module注释(提供的vimrc会自动生成此模板)
Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
"""
This module provide configure file management service in i18n environment.
Authors: jiangjinpeng(jiangjinpeng@baidu.com)
Date: 2014/04/05 17:23:06
"""
function注释
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.
Retrieves rows pertaining to the given keys from the Table instance
represented by big_table. Silly things may happen if
other_silly_variable is not None.
Args:
big_table: An open Bigtable Table instance.
keys: A sequence of strings representing the key of each table row
to fetch.
other_silly_variable: Another optional variable, that has a much
longer name than the other args, and which does nothing.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{'Serak': ('Rigel VII', 'Preparer'),
'Zim': ('Irk', 'Invader'),
'Lrrr': ('Omicron Persei 8', 'Emperor')}
If a key from the keys argument is missing from the dictionary,
then that row was not found in the table.
Raises:
IOError: An error occurred accessing the bigtable.Table object.
"""
pass
class注释
class SampleClass(object):
"""Summary of class here.
Longer class information....
Longer class information....
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
"""Performs operation blah."""
TODO注释
TODO: Improve performance using concurrent operation. $jiangjinpeng$2014-04-05$
3.8. import格式
·[强制] [PY037] 每行只能导入一个库
·[强制] 必须按如下顺序排列import,每部分之间留一个空行
标准库
第三方库
应用程序自有库
YES:
import os
import sys
from third.party import lib
from third.party import foobar as fb
import my.own.module
NO:
import os, sys
from third.party import lib, foobar
3.9. 命名规则
·[强制] [PY039] 类(包括异常)名使用首字母大写驼峰式命名
·[强制] 常量使用全大写字母,单词间用下划线分隔
·[强制] 其它情况(目录/文件/package/module/function/method/variable/parameter)一律使用全小写字母,单词间用下划线分隔
·[强制] protected成员使用单下划线前缀,private成员使用双下划线前缀
·[强制] [PY043] 禁止使用双下划线开头,双下划线结尾的名字(类似__init__)
解释
protected/private名字解释
单/双下划线开头的函数或类不会被from module import *导入
双下划线开头的类成员函数/变量会被python内部改写,加上类名前缀。以避免与派生类同名成员冲突
单/双下划线都不能真正意义上阻止用户访问,只是module开发者与使用者之间的”约定”
双下划线开头、结尾的名字对python解释器有特殊意义,可能与内部关键字冲突
示例
ClassName, ExceptionName
GLOBAL_CONSTANT_NAME, CLASS_CONSTANT_NAME,
module_name, package_name, method_name, function_name, global_var_name, instance_var_name, function_parameter_name, local_var_name
_InternalClassName, _INTERNAL_CONSTANT_NAME, _internal_function_name, _protected_member_name, __private_member_name
- 编程实践
4.1. python解释器
·[建议] 模块的主程序必须以#!/usr/bin/env python开头。如果明确只支持某个python版本,请带上python版本号
·[建议] 模块可以自带某个特定版本的python环境一起发布。需要在程序的启动脚本中指定具体使用的python解释器程序
·[建议] 推荐使用2.7版本(含)以上的python解释器
解释
python的安装位置在不同服务器上可能有差异,所以建议用env python的方式启动,使用当前环境下的默认python
百度不同服务器上安装的python版本可能有差异,如果对python程序的可移植性要求很高,可以自带python环境发布
Python官网的说明:Python 2.x is legacy, Python 3.x is the present and future of the language。但python2和3的差异很大,还有很多程序只支持2.x。而2.7是2.x系列的最后一个版本。因此,我们推荐如果使用2.x系列,尽量选择2.7。
4.2. 文件编码
·[强制] 如果文件包含非ASCII字符,必须在文件前两行标明字符编码。
·[强制] 只能使用UTF-8或GB18030编码。推荐使用UTF-8编码,如果项目确有需要,可以使用GB18030
解释
python默认使用ASCII编码解析代码文件。若代码中含有非ASCII字符,无论在字符串中还是注释中,都会导致python异常。必须在文件头标明,告知python解释器正确的字符编码
UTF8编码是自同步编码,进行字符串处理更方便、通用。但百度内部各种代码和数据都以GB18030编码为主,因此也允许使用GB18030编码。但需要注意GB18030字符串在子串查找时可能匹配到非字符边界。进行此种操作时建议转换到unicode或utf-8处理。
-- coding: utf-8 --
4.3. 类继承
·[强制] [PY046] 如果一个类没有基类,必须继承自object类。
解释
若不继承自object,property将不能正常工作,且与python3不兼容。
示例
YES:
class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
class ChildClass(ParentClass):
"""Explicitly inherits from another class already."""
NO:
class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
4.4. 字符串格式化与拼接
·[强制] 除了a+b这种最简单的情况外,应该使用%或format格式化字符串。
解释
复杂格式化使用%或format更直观
Yes: x = a + b
x = '%s, %s!' % (imperative, expletive)
x = '{}, {}!'.format(imperative, expletive)
x = 'name: %s; score: %d' % (name, n)
x = 'name: {}; score: {}'.format(name, n)
No: x = '%s%s' % (a, b) # use + in this case
x = '{}{}'.format(a, b) # use + in this case
x = imperative + ', ' + expletive + '!'
x = 'name: ' + name + '; score: ' + str(n)
·[强制] 不要使用+=拼接字符串列表,应该使用join。
解释
python中字符串是不可修改对象。每次+=会创建一个新的字符串,性能较差。
Yes: items = ['
%s, %s |
employee_table = ''.join(items)
No: employee_table = '
%s, %s |
4.5. 文件和socket
·[强制] 用完文件或socket后必须显式关闭句柄。建议使用with语法简化开发
解释
虽然文件或socket对象析构时会自动关闭文件,但python什么时候清理对象是不确定的。依赖自动回收可能导致文件句柄耗尽
示例
with open("hello.txt") as hello_file:
for line in hello_file:
print line
4.6. 主程序
·[强制] 所有module都必须可导入。如需要执行主程序,必须检查__name__ == 'main'
解释
若模块不可导入会导致pydoc或单测框架失败
示例
def main():
...
if name == 'main':
main()
4.7. 单元测试
·[建议] 推荐使用PyUnit做单元测试。是否需要做单元测试以及目标单测覆盖率由项目负责人自行决定。
·[建议] 推荐测试代码放在单独的test目录中。如果被测试代码文件名为xxx.py,那么测试代码文件应该被命名为xxx_test.py
示例
简单的sqrt测试: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()
参考
*PyUnit - the standard unit testing framework for Python
4.8. 日志输出
·[建议] 推荐使用python自带的logging库打印日志。
·[建议] 推荐默认日志格式:"%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d * %(thread)d %(message)s", 时间格式:"%Y-%m-%d %H:%M:%S"
·[建议] 推荐线上程序使用两个日志文件:一个专门记录warning/error/critical日志,另一个记录所有日志。
解释
日志格式尽量与百度传统的ullog/comlog保持一致。方便统一日志处理
独立的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)
你可以把上面的代码拷贝到自己的项目中。在程序初始化时,调用init_log即可使日志打印符合规范
import log
def main():
log.init_log("./log/my_program") # 日志保存到./log/my_program.log和./log/my_program.log.wf,按天切割,保留7天
logging.info("Hello World!!!")
参考
*logging — Logging facility for Python
- 工具支持
对所有标记的有“eagle支持”的规则,大家可以本地调用客户端检查代码是否符合这部分规则,另外在发起代码评审(cooder)时也将自动触发检查,结果会以行间评论的形式插入到代码评审中,帮助作者检查代码、帮助评审人评审代码,详细内容点击查看
Python代码规范和命名规范
前言
Python 学习之旅,先来看看 Python 的代码规范,让自己先有个意识,而且在往后的学习中慢慢养成习惯
目录
Python代码规范
一、简明概述
1、编码
如无特殊情况, 文件一律使用 UTF-8 编码
如无特殊情况, 文件头部必须加入#--coding:utf-8--标识
2、代码格式
2.1、缩进
统一使用 4 个空格进行缩进
2.2、行宽
每行代码尽量不超过 80 个字符(在特殊情况下可以略微超过 80 ,但最长不得超过 120)
理由:
这在查看 side-by-side 的 diff 时很有帮助
方便在控制台下查看代码
太长可能是设计有缺陷
2.3、引号
简单说,自然语言使用双引号,机器标示使用单引号,因此 代码里 多数应该使用 单引号
自然语言 使用双引号 "..."
例如错误信息;很多情况还是 unicode,使用u"你好世界"
机器标识 使用单引号 '...'
例如 dict 里的 key
正则表达式 使用原生的双引号 r"..."
文档字符串 (docstring) 使用三个双引号 """......"""
2.4、空行
模块级函数和类定义之间空两行;
类成员函数之间空一行;
class A:
def __init__(self):
pass
def hello(self):
pass
def main():
pass
可以使用多个空行分隔多组相关的函数
函数中可以使用空行分隔出逻辑相关的代码
2.5、编码
文件使用 UTF-8 编码
文件头部加入#--conding:utf-8--标识
3、import 语句
import 语句应该分行书写
正确的写法
import os
import sys
不推荐的写法
import sys,os
正确的写法
from subprocess import Popen, PIPE
import语句应该使用 absolute import
正确的写法
from foo.bar import Bar
不推荐的写法
from ..bar import Bar
import语句应该放在文件头部,置于模块说明及docstring之后,于全局变量之前;
import语句应该按照顺序排列,每组之间用一个空行分隔
import os
import sys
import msgpack
import zmq
import foo
导入其他模块的类定义时,可以使用相对导入
from myclass import MyClass
如果发生命名冲突,则可使用命名空间
import bar
import foo.bar
bar.Bar()
foo.bar.Bar()
4、空格
在二元运算符两边各空一格[=,-,+=,==,>,in,is not, and]:
正确的写法
i = i + 1
submitted += 1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
不推荐的写法
i=i+1
submitted +=1
x = x2 - 1
hypot2 = xx + yy
c = (a+b) (a-b)
函数的参数列表中,,之后要有空格
正确的写法
def complex(real, imag):
pass
不推荐的写法
def complex(real,imag):
pass
函数的参数列表中,默认值等号两边不要添加空格
正确的写法
def complex(real, imag=0.0):
pass
不推荐的写法
def complex(real, imag = 0.0):
pass
左括号之后,右括号之前不要加多余的空格
正确的写法
spam(ham[1], {eggs: 2})
不推荐的写法
spam( ham[1], { eggs : 2 } )
字典对象的左括号之前不要多余的空格
正确的写法
dict['key'] = list[index]
不推荐的写法
dict ['key'] = list [index]
不要为对齐赋值语句而使用的额外空格
正确的写法
x = 1
y = 2
long_variable = 3
不推荐的写法
x = 1
y = 2
long_variable = 3
5、换行
Python 支持括号内的换行。这时有两种情况。
1) 第二行缩进到括号的起始处
foo = long_function_name(var_one, var_two,
var_three, var_four)
2) 第二行缩进 4 个空格,适用于起始括号就换行的情形
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
使用反斜杠\换行,二元运算符+ .等应出现在行末;长字符串也可以用此法换行
session.query(MyTable). filter_by(id=1). one()
print 'Hello, ' '%s %s!' % ('Harry', 'Potter')
禁止复合语句,即一行中包含多个语句:
正确的写法
do_first()
do_second()
do_third()
不推荐的写法
do_first();do_second();do_third();
if/for/while一定要换行:
正确的写法
if foo == 'blah':
do_blah_thing()
不推荐的写法
if foo == 'blah': do_blash_thing()
6、docstring
docstring 的规范中最其本的两点:
所有的公共模块、函数、类、方法,都应该写 docstring 。私有方法不一定需要,但应该在 def 后提供一个块注释来说明。
docstring 的结束"""应该独占一行,除非此 docstring 只有一行。
"""Return a foobar
Optional plotz says to frobnicate the bizbaz first.
"""
"""Oneline docstring"""
二、注释
1、注释
1.1、块注释
“#”号后空一格,段落件用空行分开(同样需要“#”号)
块注释
块注释
块注释
块注释
1.2、行注释
至少使用两个空格和语句分开,注意不要使用无意义的注释
正确的写法
x = x + 1 # 边框加粗一个像素
不推荐的写法(无意义的注释)
x = x + 1 # x加1
1.3、建议
在代码的关键部分(或比较复杂的地方), 能写注释的要尽量写注释
比较重要的注释段, 使用多个等号隔开, 可以更加醒目, 突出重要性
app = create_app(name, options)
=====================================
请勿在此处添加 get post等app路由行为 !!!
=====================================
if name == 'main':
app.run()
2、文档注释(Docstring)
作为文档的Docstring一般出现在模块头部、函数和类的头部,这样在python中可以通过对象的__doc__对象获取文档.
编辑器和IDE也可以根据Docstring给出自动提示.
文档注释以 """ 开头和结尾, 首行不换行, 如有多行, 末行必需换行, 以下是Google的docstring风格示例
-- coding: utf-8 --
"""Example docstrings.
This module demonstrates documentation as specified by the Google Python Style Guide
_. Docstrings may extend over multiple lines. Sections are created
with a section header and a colon followed by a block of indented text.
Example:
Examples can be given using either the Example
or Examples
sections. Sections support any reStructuredText formatting, including
literal blocks::
$ python example_google.py
Section breaks are created by resuming unindented text. Section breaks
are also implicitly created anytime a new section starts.
"""
不要在文档注释复制函数定义原型, 而是具体描述其具体内容, 解释具体参数和返回值等
不推荐的写法(不要写函数原型等废话)
def function(a, b):
"""function(a, b) -> list"""
... ...
正确的写法
def function(a, b):
"""计算并返回a到b范围内数据的平均值"""
... ...
对函数参数、返回值等的说明采用numpy标准, 如下所示
def func(arg1, arg2):
"""在这里写函数的一句话总结(如: 计算平均值).
这里是具体描述.
参数
----------
arg1 : int
arg1的具体描述
arg2 : int
arg2的具体描述
返回值
-------
int
返回值的具体描述
参看
--------
otherfunc : 其它关联函数等...
示例
--------
示例使用doctest格式, 在`>>>`后的代码可以被文档测试工具作为测试用例自动运行
>>> a=[1,2,3]
>>> print [x + 3 for x in a]
[4, 5, 6]
"""
文档注释不限于中英文, 但不要中英文混用
文档注释不是越长越好, 通常一两句话能把情况说清楚即可
模块、公有类、公有方法, 能写文档注释的, 应该尽量写文档注释
三、命名规范
1、模块
模块尽量使用小写命名,首字母保持小写,尽量不要用下划线(除非多个单词,且数量不多的情况)
正确的模块名
import decoder
import html_parser
不推荐的模块名
import Decoder
2、类名
类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头
class Farm():
pass
class AnimalFarm(Farm):
pass
class _PrivateFarm(Farm):
pass
将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块.
3、函数
函数名一律小写,如有多个单词,用下划线隔开
def run():
pass
def run_with_env():
pass
私有函数在函数前加一个下划线_
class Person():
def _private_func():
pass
4、变量名
变量名尽量小写, 如有多个单词,用下划线隔开
if name == 'main':
count = 0
school_name = ''
常量采用全大写,如有多个单词,使用下划线隔开
MAX_CLIENT = 100
MAX_CONNECTION = 1000
CONNECTION_TIMEOUT = 600
5、常量
常量使用以下划线分隔的大写命名
MAX_OVERFLOW = 100
Class FooBar:
def foo_bar(self, print_):
print(print_)