本系列为《编写高质量代码-改善Python程序的91个建议》的读书笔记。
温馨提醒:在阅读本书之前,强烈建议先仔细阅读:PEP规范,增强代码的可阅读性,配合优雅的pycharm编辑器(开启pep8
检查)写出规范代码,是Python
入门的第一步。
Python
基础语法,即Python
程序的基本要素,分为:
- 基本数据类型:数字、字符串、列表、字典、集合、元组等;
- 常见的语法:条件、循环、函数、列表解析等。
建议19:有节制地使用from…import语句
Python
提供了3种方式引入外部模块:import
语句,from...import...
及__import__
函数。
__import__
函数可以显式地将模块名称作为字符串传递并赋值给命名空间的变量。
- 在使用
import
时需要注意以下事项:
1)一般尽量优先使用
import a
形式,如果访问B
时需要使用a.B
的形式;
2)有节制地使用from a import B
形式,可以直接访问B
;
3)尽量避免使用from a import *
,减少污染命名空间。
Python
的import
机制:Python
在初始化运行环境的时候会预先加载一批内建模块到内存中,其相关信息被存放在sys.modules
中。
from a import ...
无节制的使用产生的问题:
1)命名空间的冲突;
文件a.py
:
def add():
print "add in module A."
文件b.py
:
def add():
print "add in module B."
测试文件importtest.py
:
from a import add
from b import add
if __name__ == '__main__':
add()
2)循环嵌套导入的问题。
- 可以考虑
from...import
的情况:
1)当只需要导入部分属性或方法时;
2)模块中的这些属性和方法访问频率较高导致使用“模块名.名称”的形式进行访问过于烦琐时;
3)模块的文档明确说明需要使用from...import
形式,导入的是一个包下面的子模块,且使用from...import
形式能够更为简单和便捷时。
建议20:优先使用absolute import来导入模块
在Python2.4
以前默认为隐式的relative import
,局部范围的模块将覆盖同名的全局范围的模块。Python2.5
后虽然默认的仍是relative import
,但它为absolute import
提供了一种新的机制,在模块中使用from __future__ import absolute_import
语句进行说明后再进行导入。同时还通过点号**.
**提供了一种显式进行relative import
的方法。
相比于absolute import
,relative import
在实际应用中反馈的问题较多(Python3
中已移除),absolute import
的可读性和出现问题后的可跟踪性更好,因此,推荐优先使用absolute import
。
建议21:i+=1不等于++i
Python
解释器会将++i
操作解释为+(+i)
,其中+
表示正数符号。对于--i
也是类似。
- 实例一
>>> i=1
>>> ++i
1
- 实例二:无限循环
i = 0
ls = [1, 2, 3, 4, 5, 6]
while i < len(ls):
print ls[0]
++i
建议22:使用with自动关闭资源
with
语句的语法:
with 表达式 [as 目标]:
代码块
- 包含
with
语句的代码块执行过程如下:
1)计算表达式的值,返回一个上下文管理器对象;
2)加载上下文管理器对象的__exit__()
方法以备后用;
3)调用上下文管理器对象的__enter__()
方法;
4)若with
语句中设置了目标对象,则将__enter__()
方法的返回值赋值给目标对象;
5)执行with
中的代码块;
6)若步骤5)中的代码正常结束,调用上下文管理器对象的__exit__()
方法,其返回值直接忽略;
7)若步骤5)中的代码执行过程中发生异常,调用上下文管理器对象的__exit__()
方法,并将异常类型,值及traceback
信息作为参数传递给__exit__()
方法。若__exit__()
的返回值为false,则异常会被重新抛出;若__exit__()
的返回值为true
,则异常会被挂起,程序继续执行。
建议23:使用else子句简化循环(异常处理)
def is_prime(number):
"""
:param number:
:return:
"""
for i in xrange(2, number):
for j in xrange(2, i):
if i % j == 0:
break
else:
print '{0} is a prime number.'.format(i)
当循环“自然”终结(循环条件为假)时else
从句会被执行一次;当循环是由break
语句得到中断时,else
子句就不被执行。
建议24:遵循异常处理的几点原则
Python
中常用的异常处理语法是:try
,except
,else
,finally
,可以有多种组合。
- 异常处理流程图如下:
- 异常处理遵循的基本原则:
1)注意异常的粒度,不推荐在
try
中放入过多的代码;
2)谨慎使用单独的except
语句处理所有异常,最好能定位具体的异常;
3)注意异常捕捉的顺序,在合适的层次处理异常;向上层传递的时候需要警惕异常被丢失的情况,可以使用不带参数的raise
来传递;
4)使用更为友好的异常信息,遵循异常参数的规范。
建议25:避免finally中可能发生的陷阱
无论try
语句中是否有异常抛出,finally
语句总会被执行。
# -*-coding:UTF-8 -*-
def test(a):
try:
if a <= 0:
raise ValueError("data can not be negative.")
else:
return a
except ValueError as ex:
print ex
finally:
print "The end!"
return -1
print test(0)
print test(2)
建议26:深入理解None,正确判断对象是否为空
Python
中以下数据会被当作空处理:
- 常量
None
; - 常量
False
; - 任何形式的数值类型零,如
0
,0L
,0.0
,0j
; - 空的序列,如
‘’
,()
,[]
; - 空字典,如
{}
; - 当用户定义的类中定义了
nonzero()
方法和len()
方法,并且该方法返回整数0
或者布尔值False
。
注意:None
的特殊性体现在它既不是0
,False
,也不是空字符串,它就是一个空值对象;其数据类型为NoneType
,遵循单例模式,是唯一的,因而不能创建None
对象。所有赋值为None
的变量都相等,并且None
与任何其他非None
的对象比较结果都是False
。
>>> id(None)
140735411631784
>>> None == 0
False
>>> None == ""
False
>>> a = None
>>> id(a)
140735411631784
>>> b = None
>>> a == b # 所有赋值为`None`的变量都相等
True
- 实例:列表判空
ls = []
if ls is not None:
print "ls is: ", ls
esle:
print "ls is None"
- 以上程序运行输出为:
ls is: []
,显然不是我们的预期结果。应修正为:
ls = []
if ls:
print "ls is: ", ls
esle:
print "ls is None"
建议27:连接字符串优先使用join而不是+
1)使用操作符+
连接字符串的方法
>>> str1, str2, str3 = "testing ", "string ", "concatenation "
>>> str1 + str2 + str3
'testing string concatenation '
2)使用join
方法连接字符串的方法
>>> ''.join([str1, str2, str3])
'testing string concatenation '
- 性能测试函数
# -*-coding:UTF-8 -*-
import timeit
str_list = ["It is a long value string will not keep in memory "
for n in xrange(10000)]
def join_test():
return ''.join(str_list)
def plus_test():
res = ''
for i, v in enumerate(str_list):
res += v
return res
if __name__ == '__main__':
join_timer = timeit.Timer("join_test()", "from __main__ import join_test")
print join_timer.timeit(number=10) # 0.00255298614502
print join_timer.timeit(number=100000) # 13.4903669357
plus_timer = timeit.Timer("plus_test()", "from __main__ import plus_test")
pr