- 1 建议 8:利用 assert 语句来发现问题
- 2 建议 9:数据交换值的时候不推荐使用中间变量
- 3 建议 10:充分利用 Lazy evaluation 的特性
- 4 建议 11:理解枚举替代实现的缺陷
- 5 建议 12:不推荐使用 type 来进行类型检查
- 6 建议 13:尽量转换为浮点类型后再做除法
- 7 建议 14:警惕 eval() 的安全漏洞
- 8 建议 15:使用 enumerate() 获取序列迭代的索引和值
- 9 建议 16:分清 == 与 is 的适用场景
- 10 建议 17:考虑兼容性,尽可能使用 Unicode
- 11 建议 18:构建合理的包层次来管理 module
- 12 细节补充:
1 建议 8:利用 assert 语句来发现问题
断言(assert)基本语法:assert expression1, ["," expression2]
x, y = 1, 2
assert x == y, "not equal"
适合场景:
- 正常情况下总是为真的场合
- 需要确认函数返回值是否合理
- 会影响到后续逻辑的先决条件
注意事项:
- 断言对性能有一定的影响
- 不要使用断言来检查用户的输入
- 优先使用Python自带的异常处理功能
- 运行脚本的时候加上参数
-O
,可忽略与断言相关的语句
2 建议 9:数据交换值的时候不推荐使用中间变量
x, y = y, x
3 建议 10:充分利用 Lazy evaluation 的特性
真正需要执行的时候才计算表达式的值
- 避免不必要的计算,带来性能上的提升。例如
if x and y
,在 x 为 false 的情况下 y 表达式的值将不再计算 - 节省空间,使得无限循环的数据结构成为可能。例如斐波那契的生成器
4 建议 11:理解枚举替代实现的缺陷
枚举适用于值是固定且有限的变量,比如描述星期、月份等待。
现在基本不会考虑枚举替代的事情了,所有本小节内容暂略
但补充一些目前枚举的两种常用方式
5 建议 12:不推荐使用 type 来进行类型检查
- Python 解释器会在运行时自动进行类型检查并根据需要进行隐式类型转换
- 基于内建类型扩展的用户自定义类型,type 函数并不能准确返回结果
- 在古典类中,所有类的实例的 type 值都相等
- 可以使用
isinstance()
函数来检测类型
a = 2
isinstance (a,int) #True
isinstance (a,str) #False
isinstance (a,(str,int,list)) #True
6 建议 13:尽量转换为浮点类型后再做除法
这个问题在 Python3 中已经不存在了
<code>decimal</code> 提供精准的浮点运算
7 建议 14:警惕 eval()
的安全漏洞
- 如果使用对象不是信任源,应该避免使用 eval
- 在需要使用 eval 的地方可用安全性更好的
ast.literal_eval
替代
8 建议 15:使用 enumerate()
获取序列迭代的索引和值
seq = ['one', 'two', 'three']
for i, element in enumerate(seq):
print(i, element)
#output
0 one
1 two
2 three
9 建议 16:分清 == 与 is 的适用场景
- is 是比较两个对象在内存中是否拥有同一块内存空间
- == 是用来检验两个对象的值是否相等的
前者是相等性比较,后者是一致性校验
10 建议 17:考虑兼容性,尽可能使用 Unicode
locale.getdefaultlocale()
返回的是系统默认编码:Linux中是utf-8,Windows中是gbk
sys.getdefaultencoding()
返回的是Python默认编码:Python3是utf-8,Python2是ascii
- 读取文件的默认编码格式都由当下的读取环境/编辑器决定
- Python的
open()
函数使用的是操作系统的默认编码
11 建议 18:构建合理的包层次来管理 module
- 本质上每一个 Python 文件都是一个模块
- 而包的作用是合理组织代码、避免命名冲突
- 包通过文件
__init__.py
和普通目录区分 - 在
__init__.py
文件中定义__all__
变量,可以控制需要导入的子包或者模块
12 细节补充:
12.1 Python的字节码分析
Python 的字节码是一种类似汇编指令的中间语言,但是一个字节码指令并不是对应一个机器指令。通过 dis 模块可以进行分析:
import dis
def swap1():
x, y = 2, 3
x, y = y, x
结果如下:
def swap2():
x, y = 2, 3
temp = x
x = y
y = temp
dis.dis(swap1)
dis.dis(swap2)
结果如下:
结果分析:
通过字节码可以看出,swpa1 对应的字节码中有 2 个 LOAD_FAST
指令、2 个 STORE_FAST
指令和 1 个 ROT_TWO
指令,而 swap2 函数对应的共生成了 3 个 LOAD_FAST
指令和 3 个 STORE_FAST
指令。而指令 ROT_TWO
的主要作用是交换两个栈的最顶层元素,它比执行一个 LOAD_FAST + STORE_FAST
指令更快。
12.2 itertools.islice()
可以对迭代器做切片
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
from itertools import islice
print list(islice(fib(), 5))
12.3 枚举的两种常用方式
- 直接使用Enum列出多个枚举值来创建枚举类
import enum
# 定义Season枚举类
Season = enum.Enum('Season', ('SPRING', 'SUMMER', 'AUTUMN', 'WINTER'))
# 直接访问指定枚举
print(Season.SPRING)
# 访问枚举成员的变量名
print(Season.SPRING.name)
# 访问枚举成员的值
print(Season.SPRING.value)
# 根据枚举变量名访问枚举对象
print(Season['WINTER']) # Season.WINTER
# 根据枚举值访问枚举对象
print(Season(2)) # Season.SUMMER
# 遍历Season枚举的所有成员
for name, member in Season.__members__.items():
print(name, '=>', member, ',', member.value)
#output
Season.SPRING
SPRING
1
Season.WINTER
Season.SUMMER
SPRING => Season.SPRING , 1
SUMMER => Season.SUMMER , 2
AUTUMN => Season.AUTUMN , 3
WINTER => Season.WINTER , 4
- 通过继承Enum基类来派生枚举类
import enum
class Sex(enum.Enum):
MALE = '男', '纯爷们'
FEMALE = '女', '纯姐们'
def __init__(self, cn_name, desc):
self._cn_name = cn_name
self._desc = desc
@property
def desc(self):
return self._desc
@property
def cn_name(self):
return self._cn_name
# 访问FEMALE的name
print('FEMALE的name:', Sex.FEMALE.name)
# 访问FEMALE的value
print('FEMALE的value:', Sex.FEMALE.value)
# 访问自定义的cn_name属性
print('FEMALE的cn_name:', Sex.FEMALE.cn_name)
# 访问自定义的desc属性
print('FEMALE的desc:', Sex.FEMALE.desc)
#output
FEMALE的name: FEMALE
FEMALE的value: ('女', '纯姐们')
FEMALE的cn_name: 女
FEMALE的desc: 纯姐们