python一些不为人知的小技巧
startswith()和endswith()参数可以是元组
当检测字符串开头或结尾时,如果有多个检测值,可以用元组作为startswith()和endswith()参数:
1 | # bad |
enumerate()设置start参数做为索引起始值
当用enumerate()迭代同时要得到索引时,可以设置start参数作为索引起始值:
1 | # bad |
对切片命名
当代码中到处都是硬编码的切片索引时,我们的代码将变得无法阅读。可以对切片命名解决此问题:
1 | record = '....................100.................513.25......' |
作为一条基本准则,代码中如果有很多硬编码的索引值,将导致可读性合可维护性都不佳。一般来说,内置的slice()函数会创建一个切片对象,可以用在任何允许进行切片操作的地方。例如:
1 | >>> items = [0, 1, 2, 3, 4, 5, 6] |
上下文管理器可以同时管理多个资源
假设你要读取一个文件的内容,经过处理以后,写入到另一个文件。你能写出pythonic的代码,所以你使用了上下文管理器,满意地的写出了下面这样的代码:
1 | with open('input.txt', 'r') as source: |
你已经做的很好了,但是上下文管理器可以同时管理多个资源,上面这段代码还可以这样写:
1 | with open('input.txt', 'r') as source, open('output.txt', 'w') as target: |
else子句
Python中的else子句不仅能在if语句中使用,还能在for、while、和try语句中使用。
在for循环或是while循环正常运行完毕时(而不是通过break语句或是return语句或是异常退出循环),才会运行else块。
举个例子:
1 | >>> for i in range(3): |
如上,for循环正常结束,所以运行了后面的else块
1 | >>> for i in range(3): |
由此可以看出,for循环如果没有正常运行完毕(如上面是break结束循环的),是不会运行后面的else块。
仅当try块中没有异常抛出时才运行else块。一开始,你可能觉得没必要在try/except块中使用else子句。
毕竟,在下述代码片段中,只有dangerous_call()不抛出异常,after_call()才会执行,对吧?
1 | try: |
然而,after_call()不应该放在try块中。为了清晰明确,try块中应该只包括抛出预期异常的语句。因此,下面这种写法更优雅:
1 | try: |
现在很明确,try块防守的是dangerous_call()可能出现的错误,而不是after_call()。而且很明显,只有try块不抛出异常,才会执行after_call()。但要注意一点,else子句抛出的异常不会由前面的except子句处理,也就是说此时after_call()如果抛出异常,将不会被捕获到。
脚本与命令行结合
可以使用下面方法运行一个Python脚本,在脚本运行结束后,直接进入Python命令行。这样做的好处是脚本的对象不会被清空,可以通过命令行直接调用。
1 | $ cat hello.py |
默认字典的简单树状表达
1 | import json |
扩展拆箱(只兼容python3)
1 | >>> a, *b, c = [1, 2, 3, 4, 5] |
列表切割赋值
1 | >>> a = [1, 2, 3, 4, 5] |
命名列表切割方式
1 | >>> a = [0, 1, 2, 3, 4, 5] |
列表以及迭代器的压缩和解压缩
1 | a = [1, 2, 3] |
列表展开
1 | >>> import itertools |
生成器表达式
1 | >>> g = (x ** 2 for x in range(5)) |
字典setdefault
1 | >>> request = {} |
字典推导
1 | >>> m = {x: x ** 2 for x in range(5)} |
字典推导反转字典
1 | >>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4} |
命名元组
1 | >>> Point = collections.namedtuple('Point', ['x', 'y']) |
继承命名元组
1 | import collections |
统计在可迭代器中最常出现的元素
1 | >>> A = collections.Counter([1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 6, 7]) |
两端都可以操作的队列
1 | >>> Q = collections.deque() |
有最大长度的双端队列
1 | >>> last_three = collections.deque(maxlen=3) |
最大和最小的几个列表元素
1 | >>> import random |
两个列表的笛卡尔积
1 | >>> import itertools |
列表组合和列表元素替代组合
1 | >>> for c in itertools.combinations([1, 2, 3, 4, 5], 3): |
列表元素排列组合
1 | >>> for p in itertools.permutations([1, 2, 3, 4]): |
可链接迭代器
1 | >>> a = [1, 2, 3, 4] |
根据文件指定列类聚
1 | >>> import itertools |
按单词反转字符串
按单词反转字符串是一道很常见的面试题。在Python中实现起来非常简单
1 | def reverse_string_by_word(s): |
上面的实现其实已经能满足大多数情况,但是并不完美。比如第二个字符串中的感叹号并没有被翻转,而且原字符串中的空格数量也没有保留。(在上面的例子里其实Hello和World之间不止一个空格)
我们期望的结果应该是这样子的:
1 | print reverse_string_by_word(s) |
要改进上面的方案还不把问题复杂化,推荐使用re模块。你可以查阅re.split() 的官方文档。我们看一下具体例子
1 | >>> import re |
如果你觉得用切片将序列倒序可读性不高,那么其实也可以这样写
1 | >>> ''.join(reversed(re.split(r'(\s+|\w+)', s))) |