[关闭]
@zhengyuhong 2015-04-29T12:11:24.000000Z 字数 4481 阅读 1359

编写高质量代码-改善Python程序的91个建议

Python


第1章、引论

建议2、编写pythonic代码:

  PEP8既是一篇关于Python编码风格的指南,它提出保持代码一致性的细节要求。它至少包括对代码布局、注释、命名规范等方面的要求。在印象笔记中有中文的指南。PEP8又是一个编码风格软件

  1. sudo pip install --upgrade pep8
  2. pep8 --show--source --show-pep8 xxx.py

建议3、在代码中适当添加注释

  3)给外部可访问的函数和方法添加文档注释。注释要清楚地描述函数的功能,并对参数、返回值以及可能发生的异常进行说明,使得外部调用它d人员仅仅看docstring就能正确使用。较为复杂的内部方法也需要进行注释。推荐的函数注释如下:

  1. def FuncName(para1, para2):
  2. """
  3. Args:
  4. para1:parameter type, what is this parameter used for
  5. para2:parameter type, what is this parameter used for
  6. Returns:
  7. return type,return value
  8. """
  9. function body

  4)推荐在文件头包括copyright申明、模块描述等,如有必要,可以考虑加入作者信息以及更新记录

  1. """
  2. Licensed Materials - Property of CorpA
  3. (C) Copyright A Corp. 1999,2011 All Right Reserved
  4. ----------------------------------------------------------------
  5. File Nmae : comments.py
  6. Description : description what the main function of this file
  7. Author : Author name
  8. Change Activity :
  9. list the change activity and time and author information
  10. ----------------------------------------------------------------
  11. """

建议6、编写函数的4个原则

  原则1、函数涉及要尽量短小,嵌套层次不宜过深。
  原则2、函数申明应该做到合理、简单、易于使用
  原则3、函数参数涉及应该考虑向下兼容。如

  1. def readfile(filename):
  2. """
  3. version 1.0
  4. Args:
  5. filename:string type,location of the file
  6. Returns:
  7. None
  8. """
  9. print 'file read completed'
  1. def readfile(filename,logger):
  2. """
  3. version 2.0
  4. Args:
  5. filename:string type,location of the file
  6. Returns:
  7. None
  8. """
  9. print 'file read completed'

新的函数与原来的不兼容,调用时会发生错误,应如下修改

  1. def readfile(filename,logger=logger.info):
  2. """
  3. version 2.0
  4. Args:
  5. filename:string type,location of the file
  6. Returns:
  7. None
  8. """
  9. print 'file read completed'

  原则4、一个函数只做一件事,尽量保持函数语句粒度的一致性。

建议7、将常量集中到一个文件

建议12、不推荐使用type来进行类型检查

  内建函数type用于返回当前对象的类型,如type(1)返回。因此可以通过与Python自带模块types中所定义的名称来进行比较,根据其返回值确定变量类型是否符合要求。例如判断一个变量a是不是list类型可以使用以下代码:

  1. if type(a) is types.ListType:
  2. pass
  3. #在type模块中还有BooleanType,StringType,DictType

建议8、尽量转换为浮点类型后再做除法

  当设计浮点数运算的时候尽量先将操作数转换为浮点数类型再做运算
  浮点数比较时最后指定精度。如

  1. i = 1
  2. while i != 1.5:
  3. i += 0.1
  4. print i

陷入死循环,因为浮点数仅仅是近似表示。

建议17、考虑兼容性。尽可能使用Unicode

  Unicode为不同语言设置了唯一的二进制表示形式,可以轻易解决不同字符集之间的字符映射问题,因此可以使用Unicode作为中间介质来完成过渡转换。示例如下:

  1. with fr = open('test.txt'):
  2. print (filehandle.read().decode('utf8').encode('gbk'))

常见编码参数

编码参数 描述
'ascii' 7位ASCII码
'latin-1' or 'iso-8859-1' Latin-1,ISO-8859-1
'utf-8' 8位可变长度编码
'utf-16' 16位可变长度编码

  Python源代码中默认编码位ASCII(这点可以通过sys.getdefaultencoding()验证)。中文字符并不是ASCII字符,而此时源文件中又未指定其他编码方式,Python解释器不知道如何正确处理这种情况,便会抛出异常:SyntaxError:Non-ASCII charater '\xd6'。因此,要避免这种错误需要在源文件进行编码声明,声明可用正则表达式

  1. #coding[:=]\s*([-\w.])+
  2. #coding=utf8
  3. #coding:utf8
  4. # -*- coding:utf8 -*-

建议19、有节制地使用from ... import ...

尽量避免使用 from a import *,因为这回污染命名空间,并且无法清晰地表示导入了哪些对象。

建议22、使用with自动关闭资源

  1. with open(file,'w') as fw:
  2. blabla

建议23、使用else子句简化循环

  1. for i in range(100):
  2. if aa:
  3. break
  4. blalba
  5. else:
  6. blabla

当循环因为循环条件不再满足时执行else子句,当因为break结束循环时不执行else子句。同理while循环

  1. while condition:
  2. if aa:
  3. break
  4. else:
  5. blabla

建议29、区别对待可变对象和不可变对象

  1. def fun(l=[]):
  2. l.append(1)
  3. print l
  4. fun()
  5. fun()
  6. 输出
  7. [1]
  8. [1,1]

由于fun的参数时默认参数,默认参数在函数被调用时仅仅被评估一次,以后都会使用第一次评估结果,因此实际上对象空间里面的l所指向的时list地址,每次操作的实际指向都是同一个list地址。所以最好传入None作为默认参数。

建议33、慎用变长参数

  Python支持可变长度的参数列表,可以通过在函数定义的时候使用*args和**kwargs两个特殊语法来实现(args,kwargs可以位其他变量名)

  1. def sum_fun(*args):
  2. for arg in args:
  3. print arg
  4. sum_fun(1)
  5. sum_fun(1,2)
  6. sum_fun(1,2,3)
  7. def dict_fun(**kwargs):
  8. for k,v in kwargs.items():
  9. print k,v
  10. dict_fun()

建议37、按需选择sort()或者sorted() 

建议39、使用Counter进行计数统计

  1. from collections import Counter
  2. some_data = [1,2,3,4,5,6,6,7,5,1]
  3. cnt = Counter(some_data)
  4. cnt.most_common(topk)#找出topkkey以及对应的value

建议47、使用logging记录日志信息

  仅仅将栈信息输出到控制台是远远不够的,更为常见的是使用日志保存程序运行过程中的相关信息,如运行时间、描述信息以及错误或者异常发生的时候的特定信息。Python中自带的logging模块提供了日志功能,它将logger分为5个level,可以通过logger.setLevel(lvl)来设定,其中DEBUG为最低级别,CRITICAL为最高级别,默认为WARNING级别。

Level 使用情形
DEBUG 详细的信息,在追踪问题的时候使用
INFO 正常的信息
WARNING 一些不可预见的问题发生、或者将要发生,如磁盘低,但不影响程序的运行
ERROR 由于一些严重问题,程序功能受到影响
CRITICAL 严重的错误,或者程序本身不能够继续运行

建议55、__init__不是构造方法

  很多pythoner会这样子认为,__init__方法就是类的构造方法.因为表面上看它确实很像构造方法:当需要实例化一个对象的时候,使用a=Class(args)便可以返回一个类的实例,其中args的参数与__init__中声明的一样,但是真的是酱紫嘛?如下分析:

  1. class A(object):
  2. def __new__(cls, *args, **kwargs):
  3. print cls
  4. print args
  5. print kwargs
  6. instance = object.__new__(cls, *args, **kwargs)
  7. print instance
  8. def __init__(self, a, b):
  9. print 'init gets called'
  10. print 'self is ', self
  11. self.a, self.b = a, b

  我们原本期望会输出1,2,但是运行却抛出异常。实际上__init__不是真正意义上的构造方法,它所做的工作是初始化,就是在对象已经创建好了之后初始化它们的值,__new__才是真正创建对象。上面的程序抛出异常时因为__new__没有显式返回已经创建好的对象,因为a1是None,当访问成员数据时就会出错。
  

建议57、为什么需要self参数

  如同this->指针,没有加self就是静态成员变量,静态成员函数(C++ 中的static)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注