《编写高质量代码 改善python程序的91个建议》第二章 基础语法 19-35
建议19:有节制使用from...import...
import VS from...import....
import test ## test 进入局部命令空间 + 进入系统模块字典中 sys.module['test'];
from test import a ## a 进入局部命令空间 + test进入系统模块字典中(a 不在系统模块字典中);
## test_import.py from a import add from b import add # 最后起作用的是最近一次导入的函数,即不同模块相同函数存在覆盖情况;
# 命令空间冲突
##----嵌套导入----
from b import person # a.py
form a import info # b.py
建议20:用absolute import 来导入模块 from app.sub1 import testmodule, 相对导入 import .. import testmodule在python 3中被删除(容易造成混乱);
建议21:i +=1 不等于 ++i; ++i 语法python 解析器当作 +(+1)来处理(永远等于+1,表示正数符号)并不是自增语法; --i: - (-1) 等于1 表示两个负号;
建议22:使用 with 关闭资源;
with open("path", "rw") as filehandle [, open("path_another", "r") as rhandle]
## f.__enter__() 进入文件上下文,生成上下文管理器对象 = f
filehandle.read()
filehandle.write()
...
## f.__exit__() 离开上下文,上下文管理器对象做清理善后工作,比如关闭 handle;
## 另外,python 还提供了 contextlib 来专门管理上下文
建议23:使用 else 简化循环;
for/while expr1: do_sth()
break else: ## expr1 为 False时,执行else; 当 break 时,expr1仍然为True,此时不执行else; do_else_sth() ## 在遍历列表找值时,有用处;
建议24:异常处理基本原则
try:
statment ##----不宜放入过多操作
except <name1>: ##--- 异常处理尽量精细, 子类异常先处理
statment
except <name2,name3>: ## 异常提示要友好
statment
except:
statment ## 其他异常统一处理, 各种可能性都有可能,此处异常说明要慎重 || 也可将异常 raise 抛给上层
else:
statment ## 没有异常
finally:
statment ## 不管是否有异常,依然会执行
建议25: 小心 finally 中的陷阱
情况1: 如果 异常没有被except处理,将会临时保存起来,执行finally后再抛出;但如果finally也抛异常或者有return 或 break时,临时异常会被丢弃或者覆盖;
# 情况2:
try: if a <= 0: raise ValueError
else:
return a ## return a 之前先执行finally语句块,如果 finally 有return,那么该return a 失效
except ValueError:
print(a)
finally:
print("End")
reutrn -1;
建议26:深入理解 None, 正确判断对象为空
#### python 判断为空的情况汇总 ####
None # 是一个常量,单例对象,类名 NoneType, 任何 None 都相等,固定id, id(None)
False # 常量
0、0L、0.0 #等,任意0数值类型
'' () [] {} #空序列
即,空值有很多种,但 相互都不is相等( [] is None ==> False);
list1 = [] if list1: ## 会调用 list1.__nonzero__方法来判断,返回 0/1 或者 True/False
print(list1) ## 没有 __nonzero__ 方法,会调用 __len__ 方法 ,返回 0/1
## __nonzero__ 和 __len__ 都没有 ,返回 True
建议27:字符串连接优先考虑 join 而不是 +;
’‘.join(strlist) (一次性加起来) 优优优于于于 result += strlist[i] (逐个加起来)
python 中的字符串是不可变对象; str1 + str2 每次都要申请一块新内存来存放结果;join 是一次性申请一块总大小地址,一次性存放;
建议28:格式化字符串优于使用format 而不是 %: fomat 使用更灵活,可不按顺序。以后会直接取代%成为标准;
##--------- % 格式化 -------------
"%s" % "chaoren" ## 单个参数直接格式化
"%s %s %s" % ("Hello", "World", "chaoren") ## 元组形式
"%(key)s -- %(value)f" % dict1 ## 字典形式
##---------- format 格式化 -----------
"xxx {0} xxx {2}, xxx {1}".format("hello", "world", "!!!") ## 引用元组位置
point = (1,3)
"X:{0[0],xxx, Y:{0[0]}".fomat(point)
"test %s"%(point,)
## 格式化规则百度
(tuple) 与 (tuple,) 前者表示一个加了括号的对象, 后者表示一个元组。加一个逗号表示区分是元组。
建议29:区别对待可变对象与不可变对象
对象属性:id, type 及 值。
不可变对象:数字,字符串,元组;
可变对象:字典, 列表, 字节数组,集合;
在类函数中,默认参数仅仅被评估一次。每次分配新对象时,会使用上次的参数结果!改进:default=None, if default is None:default=[] self.default=default
【对于浅拷贝和深拷贝:对可变对象与不可变对象的之间的不同】
建议30:[] () {} 一致的容器初始化形式
##---- 列表解析 形式----##
[x for x in xlist] ##一维列表
[[x.upper() for x for x_list] for x_list in xx_list] ##二维列表
[(a,b) for a in a_list for b in b_list if a==b] ## 多重迭代
[f(x) for x in x_list if x>0] ## 遍历 for + if
[ x**2 if x%2==0 else x+1 for x in x_list if x>0] ## for + if + fun 遍历加计算
= [(x**2 if x%2==0 else x+1) for x in x_list if x>0]
## 建议用列表解析式 [ ...... ], ''.append(): 较耗时,因为每次需要开辟新空间来存储最新结果;
建议31: 函数传参既传值也不是传引用