优化python程序的建议
文章目录
1 将常量集中到一个(配置)文件
2 行长度
每行不超过80个字符
以下情况除外:
1)长的导入语句
2)注释里的url
3 不要用反斜杠连接行
python中会将圆括号、中括号和花括号将行隐式连接起来,因此,你可以再表达式外面增加一对额外的圆括号。
示例:
if(width == 0 and height == 0 and color == 'red' and emphasis == 'strong'):
4 括号
宁缺毋滥的使用括号
除非是用于实现行连接, 否则不要在返回语句或条件语句中使用括号. 不过在元组两边使用括号是可以的.
Yes: if foo: bar() while x: x = bar() if x and y: bar() if not x: bar() return foo for (x, y) in dict.items(): ...
No: if (x): bar() if not(x): bar() return (foo)
5 空行
顶级定义之间空两行,方法定义之间空一行
顶级定义:函数或者类定义
类定义与第一个方法之间,应该空一行
函数或者方法中,某些地方要是你觉得合适,就空一行
6 空格
括号内不要有空格
yes: spam(ham[1], {egg: 2}, [])
no: spam( ham[ 1 ], { egg:2 }, [ ] )
不要在逗号、分号、冒号前面加空格,但应该在他们后面加(除了在行尾)。
yes: if x == 4: print x, y
no: if x == 4 : print x , y
参数列表,索引或者切片的左括号前不加空格
yes: spam(1)
no: spam (1)
yes: dict["key"] = list[index]
no: dict["key"] = list [index]
在二元操作符两边都加上一个空格,比如赋值(=),比较(==, <, >, !=, in not in , is , is not),布尔(and, or, not)
至于算术操作符两边的空格如何使用,需要自己判断,但是两侧务必要保持一致
yes: x == 1
no: x<1
当“=”用于指示关键字参数或者默认参数时,不要在其两侧使用空格
yes: def complex(real, imag=0.0): return magic(r=real, i=img)
no: def complex(real, imag = 0.0): return magic(r = real, i = img)
不用使用空格来垂直对齐多行间的标记,因为这会造成维护的负担(适用于:, #, =等)
yes: foo = 1000 # 注释 long_name = 2 #注释不需要对齐
7 新文件的创建
新文件的创建开始以:#coding:utf-8
开头
8 main
即使是一个打算被当作脚本的文件,也应该是可导入的。并且简单的导入不应该导致这个脚本的主功能(main function)被执行,这是一种副作用,主功能应该放在一个main()函数中。
在Python中,pydoc以及单元测试要求模块必须是可导入的,你的代码应该在执行主程序前总是检查if __name__ == "__main__"
,这样当模块被导入时主程序就不会被执行
def main():
if __name__ == "__main__":
...
所有顶级代码在模块导入时都会被执行,要小心不要去调用函数,创建对象,或者执行那些不应该在使用pydoc时执行的操作。
9 python之父Guido推荐的命名规范
type | puclic | internal |
---|---|---|
classes | CapWords | _CapWords |
modules | lower_with_under | _lower_with_under |
packages | lower_with_under | |
exception | CapWords | |
functions | lower_with_under() | _lower_with_under() |
global/class contants | CAPS_WITH_UNDER | _CAPS_WITH_UNDER |
global/class varibles | lower_with_under | _lower_with_under |
instance variables | lower_with_under | _lower_with_under |
method names | lower_with_under | |
function parameters | lower_with_under | |
local variables | lower_with_under |
10 语句
通常一个语句应该占一行
不过,如果测试结果与测试语句在一行放得下,可以将它们放在一行。
如果是if语句,只在没有else时才能这样做。特别地,绝不要对try/except这样做,因为try和except不能放在同一行
yes: if foo: bar(foo)
no: if foo: bar(foo) else: baz(foo)
no: try: bar(foo) except ValueError: baz(foo)
11 导入格式
每个导入独占一行
yes:
import sys
import os
no: import sys, os
导入总是放在文件的顶部,位于模块注释和文档字符串之后,模块全局变量和常量之前。
导入应该按照从最通用到最不通用的顺序分组:
1)标准库导入
2)第三方库导入
3)应用程序制定导入
import foo
from foo imort bar
import foo.bar import baz
from foo.bar import Quux
from Foob import ar
12 文件和sockets
在文件和sockets结束时,显示的关闭它。
除文件外,sockets或者其他类似文件对象在没有必要的情况下打开,会有许多副作用,例如:
1)它们可能会消耗有限的系统资源,如文件描述符。如果这些资源在使用后没有及时归还系统,那么处理这些对象的代码会将资源消耗殆尽;
2)持有文件将会阻止对于文件的其他诸如移动、删除之类的操作。
3)仅仅是从逻辑上关闭文件和sockets,那么他们仍然可能会被其共享的程序在无意中进行读或写操作,只有当它们真正被关闭后,对于它们尝试进行读写操作将会抛出异常,并使问题快速显现出来;
而且,幻想当文件对象析构时,文件和sockets会自动关闭,试图将文件对象的生命周期和文件的状态绑定在一起的想法,都是不现实的。因为有如下的原因:
1)没有任何方法可以确保运行环境会真正地执行文件的析构,不同的python实现不同的内存管理技术,比如延时垃圾处理机制,延时垃圾处理机制可能会导致对象生命周期被任意无限制的延长;
2)对于文件意外的引用,会导致对于文件的持有时间超出预期(比如对于异常的跟踪,包含有全局变量等)
推荐使用“with 语句”来管理文件
with open(hello.txt") as f:
for line in f:
print line.strip()
13 字符串
避免在循环中使用“+”或者“+=”来累加字符串,由于字符串是不可变的,这样做会创建大量不必要的临时对象,并且导致二次方而不是线性的运行时间。
作为替代方案,可以将每个字符串加入列表,然后循环结束后使用.join()连接变量
yes:
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)
在同一个文件中,保持使用字符串引号的一致性,使用单引号或者双引号之一。在字符串内可以使用另外一种引号,以避免在字符串中使用。
14 类
如果一个类不继承自其他类,就显示的从object继承,嵌套类也一样
yes:
class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
no:
class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
继承自object是为了使属性正常工作,并且这样可以保护你的代码,使其不受python 3000的一个特殊的潜在不兼容性影响,这样做也定义一些特殊的方法,这些方法实现了对象的默认语法,包括__new__,__init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__.
15 注释
确保对模块、函数、方法和行内注释使用正确的风格
16 文档字符串
python有一种独一无二的注释方式:使用文档字符串。文档字符串是包、模块、类或者函数里的第一条语句,这些字符串可以通过对象的__doc__
成员被自动提取,并且被pydoc所用。我们对文档字符串的惯例是使用三重双引号"""。一个文档字符串应该这样组织:首先是一行以句号、问号或者惊叹号结尾的概述(或者该文档字符串只有一行),接着是一个空行,接着是文档字符串剩下的部分,它应该与文档字符串的第一行的第一个引号对齐
17 块注释和行注释
最需要协助是的是代码中哪些技巧性的部分,如果你在下次代码审查的时候必须解释一下,那么你应该现在就给它写注释,对于复杂的操作,应该在其操作开始前写上若干行注释,对于不是一目了然的代码,应该在其行尾添加注释。
为了提高代码的可读性民主是至少离开代码2个空格。
另一方面,绝对不要描述代码,假设读代码的人比你更懂python,他只是不知道你的代码要做什么
18 类注释
类应该在其定义下有一个用于描述该类的文档字符串,如果你的类有公共属性,那么文档中应该有一个属性段,并且遵守和函数参数相同的格式
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."""
19 函数和方法注释
一个函数必须有文档字符串,除非它满足以下条件:
1)外部不可见
2)非常短小
3)简单明了
文档字符串应该包含函数做什么,以及输入和输出的详细描述,通常,不应该描述“怎么做”,除非是一些复杂的算法。文档字符串应该提供足够的信息,当别人编写代码调用该函数时,他不需要看一行代码,只要看文档字符串就可以了。对于复杂的代码,在代码旁加注释会比使用文档字符串更有意义。
关于函数的几个方面应该在特定的小节中进行描述记录,这几个方面如下所述,每节应该以一个标题行开始,标题行以冒号结尾。除标题行,小节的其他内容应该被缩进2个空格。
Args:列出每个参数的名字, 并在名字后使用一个冒号和一个空格, 分隔对该参数的描述.如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致). 描述应该包括所需的类型和含义.
如果一个函数接受foo(可变长度参数列表)或者**bar (任意关键字参数), 应该详细列出foo和**bar.
Returns: (或者 Yields: 用于生成器)描述返回值的类型和语义. 如果函数返回None, 这一部分可以省略.
Raises:列出与接口有关的所有异常.
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.
ther_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