建议 8:利用assert语句来发现问题
# 断言用法
assert expression
# 等价于以下语句
if not expression:
raise AssertionError
使用断言注意以下几点
1 不要滥用,这是基本原则。若由于断言已发了异常,通常代表程序中存在bug。因此断言应该使用在正常逻辑不可能到达的地方活正常情况下总是为真的场合。
2 如果Python本身的异常能够处理就不要再使用断言。如对于类似数组越界、类型匹配、除数为0之类的错误,不建议使用断言来处理。下面的例子中使用断言就显得多余,因为如果传入的参数一个为串串,另一个为数字或者列表,本身就会抛出TypeError。
def stradd(x, y):
assert isinstance(x, basestring)
assert isinstance(y, basestring)
return x+y
3 不要使用断言来检查用户的输入。如对于一个数字类型,如果根据用户的设计,该值的范围是2~10,较好的做法是使用条件判断,并在不符合条件的时候输出错误提示信息。
4 在函数调用后,当需要确认返回值是否合理时可以使用断言
5 当条件是业务逻辑进行下去的先决条件时,可以使用断言。如list1和其副本list2,业务继续下去的条件是这两个list必须是一样的,但由于某些不可控因素,如使用了浅拷贝而且list1中含有尅安对象,就可以使用断言来判断这两者的关系,如果相等,则继续运行后面的程序意义不大。
建议9:数据交换不推荐使用中间变量
# Python推荐使用, 性能更好
x ,y = y, x
# 大家熟悉方式
temp = x
x = y
y = temp
建议10:充分利用Lazy evaluation的特性
1 避免不必要的计算,以便带来性能上的提升。比如以下示例:
# 当x为False的情况,y将不再计算
if x and y:
pass
2 节省空间,使得无限循环的数据结构成为可能。如斐波那契数列的实现
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a+b
from itertools import islice
print(list(islice(fib(), 5)))
建议11:理解枚举替代实现的缺陷
在Python3.4以前并没提供枚举类型,于是人们充分利用Python的动态性这个特征,想出来枚举的各种替代实现方式。
1 使用类属性
class Seasons:
Spring = 0
Summer = 1
Autumn = 2
Winter = 3
2 借助函数
# 定义
def enum(*posarg, **keysarg):
return type("Enum", (object,), dict(zip(posag, xrange(len(posarg))), **keysarg))
# 使用
Seasons = enum("Spring", "Summer", "Autumn", Winter=1)
print Seasons.Spring # 0
3 使用collections.namedtuple
Seasons = namedtuple("Seasons", "Spring Summer Autumn Winter")._make(range(4))
Seasons.Spring # 0
枚举代替实现方式还有很多,这些方案也有其不合理的地方。
1 枚举值不唯一
2 带来一些无意义的操作,如 Seasons.Summer + Seasons.Autumn == Seasons.Winter
还有一种替代方案,第三方模块 flufl.enum,
请参考: http://Pythonhosted.org/flufl.enum/docs/using.html
建议12: 不推荐使用type来进行类型检查
不刻意进行类型检查,而是在出错的情况下通过抛出异常来进行处理,是较为常见的方式。但实际应用中为了提高程序的健壮性,仍然会面临需要类型检查的情况。那么用什么方法呢。
1
type(), 所有基本类型对应的名称都可以在types模块中找到,如types.BooleanType, types.IntType, types.StringType等。为什么不推荐使用呢,因为基于内建类型扩展的用户自定义类型,type函数并不能准确返回结果。
if type(a) is types.ListType:
pass
2 isinstance
isinstance(2, float) # False
isinstance("a", (str, unicode)) # True
isinstance((2, 3), (str, list, tuple)) # True
建议13:尽量转换为浮点类型后再做除法
在Python2中,借鉴了C预研的一些规则,两个整数相除,返回值也是整数,运算结果将直接截断。Python3中做了一定的修正,已经不存在这个问题了。
建议14: 警惕eval()的安全漏洞
如果使用对象不是信任源,应该尽量避免使用eval,在使用eval的地方可用安全性更好的ast.literal_evalt替代。可以参考文档:http://docs.python.org/2/library/ast.html#ast.literal_eval
建议15:使用enumerate()获取序列迭代的索引和值
使用enumerate(),代码清晰简洁,可读性最好。它具有一定的惰性,每次尽在需要的时候财货产生一个(index, item)对。
li = ["a", "b", "c", "d", "e"]
for i, e in enumerate(li):
print "index: ", i, "element: ", e
enumerate()函数的内如实现非常简单,enumerate(sequence, start=0)实际相当于如下代码
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
对于字典的迭代循环,enumerate()并不适合,输出结果与期望大相径庭
person_info = {"name": "Jon", "age": "20"}
for k, v in enumerate(person_info):
print k, v
# 输出为(Mac, Python2.7.15)
0 name
1 age
要获取字典迭代过程中的key、value,应该使用如下items()方法:
for k, v in person_info.items():
print k, v
建议16:分清 == 与 is 的适用场景
1 is 表示对象标识符
2 == 表示值是否相等
# 当a、b的值同为短字符串时,a is b为True,python的字符串主流机制,他们指向同一个对象
a = "I am using long string for testing"
b = "I am using long string for testing"
a is b # False
a == b # True
建议17:考虑兼容性,尽可能使用Unicode
对于A、B两种编码系统,相互转换示意图如下
建议18:构建合理的包层次来管理module
本质上每一个Python文件都是一个模块,使用模块可以增强代码的可维护性和可重用性。
什么是包呢?简单的说包即是目录,它除了包含常规的Python文件以外,还包含一个__init__.py
1 合理组织代码,便于维护和使用
2 能够有效地避免名称空间冲突
3 以下一个可供参考的Python项目结构
ProjectName/
| --- README
| --- LICENSE
| --- setup.py
| --- sample/
| | --- __init__.py
| | --- core.py
| | --- helpers.py
| --- docs/
| | --- config.py
| | --- index.rst
| --- bin/
| --- package/
| | --- __init__.py
| | --- subpackage/
| | --- ......
| --- tests/
| | --- test_basic.py
| | --- test_advanced.py