1. 知道你使用的python的版本
-
命令行
python -version
-
程序内
import sys print(sys.version_info) print(sys.version)
2. 遵循PEP8的风格
Python Enhancement Proposal #8
- 在线文档 http://www.python.org/dev/peps/pep-0008/
规则例子
space空格
- 缩进使用spaces 代替 tabs
- 每一级的缩进使用四个spaces
- 每行79个字符或者更少
- 长表达式换行也要缩进四个空格
- 函数与函数、类与类之间 必须被两个空行分开
- 类的函数需要被一行空行分开
- 不要在列表索引、函数调用或关键字参数赋值周围放空格
- 在变量赋值的前后放且仅放一个空格
命名
- 函数、变量和属性应采用小写下划线格式
- 私有实例属性应该采用__double_leading_下划线格式
- 类和异常应该大写
- 模块级常量应该是ALL_CAPS格式
- 类中的实例方法应该使用self作为第一个参数的名称
- 类方法应该使用cls作为第一个参数的名称
表达式和语句
- 使用内联否定(
if a is not b
)代替肯定的否定(if not a is b
) -
不要通过检查长度来检查空值(如[]或")(
if len(somelist) == 0
), 而使用if not somelist
- 同上条,非空值使用
if somelist
- 避免单行的 if for while
-
import
总放在最前面 - 在导入模块时,总是使用模块的绝对名称
- 如果必须进行相对导入,则使用显式语法
from . import foo
- 导入应该按以下顺序分节:标准库模块,第三方模块,你自己的模块。
3. 知道 bytes、str和unicode之间的差别
字符串的表达
版本 | 表达方法 |
---|---|
python2 | unicode 和 str |
python3 | bytes 和 str |
区分
- 有很多的方法可以将Unicode转为二进制数据,最常见的方法就是UTF-8
- python 3 中的
str
实例 和python2 中的unicode
实例没有相关的二进制编码- Unicode–>二进制:encode
- 二进制–>Unicode: decode
确保类型返回一致
- python3
# 始终返回str
def to_str(bytes_or_str):
return bytes_or_str.decode('utf-8') if isinstance(bytes_or_str,bytes) else bytes_or_str
# 始终返回bytes
def to_bytes(bytes_or_str):
return bytes_or_str.encode('utf-8') if isinstance(bytes_or_str,str) else bytes_or_str
4. 写辅助函数而不是复杂函数表达式
可读性
在python中很容易写一个表达式完成功能。例如解析URL
from urllib.parse import parse_qs
my_values = parse_qs('red=5&blue=0&green=’,
keep_blank_values=True)
print(repr(my_values))
>>>
{‘red’: [‘5’], ‘green’: [”], ‘blue’: [‘0’]}
目标:获得qs中第一个数字,没有则返回0
- 复杂的表达式
red = my_values.get(‘red’, [”])
red = int(red[0]) if red[0] else 0
- 辅助函数
def get_first_int(values, key, default=0):
found = values.get(key, [”])
if found[0]:
found = int(found[0])
else:
found = default
return found
5. 知道如何对序列进行切片
-
切片可以很简单的实现于 list、str和bytes
-
切片可以扩展到任何实现了
__getitem__
和__setitem__
特殊方法的Python类 -
基本语法
somelist[start:end]
-
切片之后不是同一对象
>>> a=[1,2,3,4,5,6] >>> b=a[2:] >>> b[2]=c >>> id(a) 1991199427968 >>> id(b) 1991199443264 >>> a [1, 2, 3, 4, 5, 6] >>> b [3, 4, 99, 6]
6. 避免在单个切片中使用start、end和stride
-
stride 为 -1 常用于反转字符串
>>> a='abcdefg' >>> a[::-1] 'gfedcba'
- 但是遇到非ASC-II编码时候容易出错
>>> w='谢谢' >>> x=w.encode('utf-8') >>> x[::-1].decode('utf-8') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa2 in position 0: invalid start byte
-
尽量不要同时使用负值,难以理解
7. 使用列表推导式而不是map和filter
# 求 a 中 偶阶方
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 1. 简单的方法 列表推倒式子
even_square = [x**2 for x in a if x%2==0] # [4, 16, 36, 64, 100]
#2. 复杂的map搭配filter方法
alt=map(lambda x: x**2, filter(lambda x: x%2 ==0,a))
- 字典和set中的列表推导式
# 反转字典
res_dict={value,key for key,value in dict}
8. 避免在列表表达式中使用2个以上的表达式
难以阅读
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
filtered = [[x for x in row if x % 3 == 0]
for row in matrix if sum(row) >= 10]
print(filtered)
>>>
[[6], [9]]
9. 考虑用于大型推导式的生成器表达式
-
第七点中如果用于读取文件中的行数
value = [len(x) for x in open(‘/tmp/my_file.txt’)]
- 文件大的时候会占用大量内存
-
使用生成器进行替代
it = (len(x) for x in open(‘/tmp/my_file.txt’)) print(it) >>> <generator object <genexpr> at 0x101b81480> print(next(it)) print(next(it)) >>> 100 57
-
生成器的另一个强大之处在于可以结合在一起
roots = ((x, x**0.5) for x in it) print(next(roots)) >>> (15, 3.872983346207417)
10. 使用enumerate而不是range
- enumerate用惰性生成器包装任何迭代器
- 这个生成器生成一对循环索引和迭代器中的下一个值
flavor_list = ['vanilla', 'chocolate', 'pecan', 'strawberry']
#遍历列表的range 方法
for i in range(len(flavor_list)):
flavor = flavor_list[i]
print(‘%d: %s’ % (i + 1, flavor))
# 简便方法:enumerate
for i, flavor in enumerate(flavor_list):
print(‘%d: %s’ % (i + 1, flavor))
# 可以指定开始位置
for i, flavor in enumerate(flavor_list, 1):
print(‘%d: %s’ % (i + 1, flavor))
11. 使用zip并行处理迭代器
- zip用一个惰性生成器包装了两个或多个迭代器。
- zip生成器生成元组,其中包含每个迭代器的下一个值
names = ['Cecilia', 'Lise', 'Marie']
letters = [len(n) for n in names]
# 获得最长的名字并记下字母数
# 方法1:range
longest_name = None
max_letters = 0
for i in range(len(names)):
count = letters[i]
if count > max_letters:
longest_name = names[i]
max_letters = count
#方法2:enumerate
for i, name in enumerate(names):
count = letters[i]
if count > max_letters:
longest_name = name
max_letters = count
#方法3:zip
for name, count in zip(names, letters):
if count > max_letters:
longest_name = name
max_letters = count
- 注意点:如果您不确定zip的列表的长度是否相等,可以考虑使用itertools内置模块中的
zip_longest
函数
12.避免在for和while循环之后出现else块
else 会在正常退出时候执行,而在break时候不会执行
# else被执行
for i in range(3):
print('Loop %d' % i)
else:
print('Else block!')
>>>
Loop 0
Loop 1
Loop 2
Else block
# else 未被执行
for i in range(3):
print(‘Loop %d’ % i)
if i == 1:
break
else:
print(‘Else block!’)
>>>
Loop 0
Loop 1
13. 利用try/except/else/finally中的每个块
UNDEFINED = object()
def divide_json(path):
handle = open(path, ‘r+’) # May raise IOError
try:
data = handle.read() # May raise UnicodeDecodeError
op = json.loads(data) # May raise ValueError
value = (
op[‘numerator’] /
op[‘denominator’]) # May raise ZeroDivisionError
except ZeroDivisionError as e:
return UNDEFINED
else:
op[‘result’] = value
result = json.dumps(op)
handle.seek(0)
handle.write(result) # May raise IOError
return value
finally:
handle.close() # Always runs 无论如何都会被执行