改善python——用pythonic的方法思考

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 无论如何都会被执行
上一篇:Linux共享Window文件夹权限问题


下一篇:centos7安装libvirt支持xen