Python 高效编程小技巧

Python 一直被我拿来写算法题,小程序,因为他使用起来太方便了,各种niubi闪闪的技能点也在写算法的过程中逐渐被挖掘到,感谢万能的谷哥度娘SOF以及各大博客网站,在这里整理一二。

几句废话:

因为我是懒癌晚期,最不喜欢在文章里发图片,因为Mweb写作或者是马克飞象写作,可以直接拖图进来,但是上传博客的话,就需要考虑是使用服务器上的媒体库,还是放七牛,放七牛上还得用它的命令行工具,或者是Web端操作,想想都好麻烦。所以,本地一直存放着几篇写完的文章楞是没有上传(一篇探索红黑树,一篇是设计模式C++版半完全指南,一篇是Linux的小文章),就是因为往里边塞了太多图片的原因。所以以后写文,尽量控制图片 <= 3。

下面进入密集式正题,过于炫技的部分被我去掉了,因为我看过之后只是碎了膝盖,然而并不常用。因为自己很少做整理,现在知道整理的强大之处了,所以以后也会注意相关知识的整理。以下方法的适用场景我也就不用多说了,因为都是最最常见的场景:

1. 拆箱(这个方法比较常见,非常高效)

变量声明利用拆箱这种方式,非常高效,这也算是Python 里最常用的技巧了,也是我最开始使用 Python 时感觉非常惊奇的功能。

1
2
3
4
5
6
7
8
9
10
11
12
>>> a, b, c = 1, 2, 3
>>> a, b, c
(1, 2, 3)
>>> a, b, c = [1, 2, 3]
>>> a, b, c
(1, 2, 3)
>>> a, b, c = (2 * i + 1 for i in range(3))
>>> a, b, c
(1, 3, 5)
>>> a, (b, c), d = [1, (2, 3), 4]
>>> a, b, c, d
1, 2, 3, 4

拆箱也可用于变量交换

1
2
3
4
>>> a, b = 1, 2
>>> a, b = b, a
>>> a, b
(2,1)

2. 指定步长的切割

刚开始接触Python 的时候,被Python 深拷贝的方式逗乐了,写Python 你可以利用想象力写代码。深拷贝利用的就是这个指定步长的切割。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::2]
[0, 2, 4, 6, 8, 10]
>>> a[2:8:2]
[2, 4, 6]
# 下边这个实现深拷贝
>>> print a[::1]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 逆序拷贝
>>> print a[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# 你还可以给切割的部分赋值,也可以借此插入数组
>>> a = [1, 2, 3, 4, 5]
>>> a[2:3] = [0, 0]
>>> a
[1, 2, 0, 0, 4, 5]
>>> a[1:1] = [8, 9]
>>> a
[1, 8, 9, 2, 0, 0, 4, 5]
# 还有命名列表切割方式
>>> a = [0, 1, 2, 3, 4, 5]
>>> last_three = slice(-3, None)
>>> last_three
slice(-3, None, None)
>>> a[last_three]
[3, 4, 5]

3. 压缩器zip

zip 这个也是靠想象力实现各种各样的功能。

  1. 列表 or 迭代器的压缩与解压缩
1
2
3
4
5
6
7
>>> a = [1, 2, 3]
>>> b = ['a', 'b', 'c']
>>> z = zip(a, b)
>>> z
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> zip(*z)
[(1, 2, 3), ('a', 'b', 'c')]
  1. 列表相邻元素压缩器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> a = [1, 2, 3, 4, 5, 6]
>>> zip(*([iter(a)] * 2))
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent = lambda a, k: zip(*([iter(a)] * k))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]
>>> zip(a[::2], a[1::2])
[(1, 2), (3, 4), (5, 6)]
>>> zip(a[::3], a[1::3], a[2::3])
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent = lambda a, k: zip(*(a[i::k] for i in range(k)))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]
  1. 用压缩器翻转字典
1
2
3
4
5
6
7
8
>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m.items()
[('a', 1), ('c', 3), ('b', 2), ('d', 4)]
>>> zip(m.values(), m.keys())
[(1, 'a'), (3, 'c'), (2, 'b'), (4, 'd')]
>>> mi = dict(zip(m.values(), m.keys()))
>>> mi
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

4. 列表展开

列表展开的方式五花八门,动用大脑可以创造各种各样的方法,最便于理解的是以下两种:

1
2
3
4
5
>>> a = [[1, 2], [3, 4], [5, 6]]
>>> sum(a, [])
[1, 2, 3, 4, 5, 6]
>>> [x for l in a for x in l]
[1, 2, 3, 4, 5, 6]

5. 生成器表达式

1
2
3
4
5
6
7
8
9
10
11
>>> g = (x ** 2 for x in xrange(10))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> sum(x ** 3 for x in xrange(10))
2025
>>> sum(x ** 3 for x in xrange(10) if x % 3 == 1)
408

6. 字典推导和集合推导

一上来我打成了推倒是什么心理,相信在平时推导过列表,他还有更多的应用方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 这个是最常见的推导
>>> list1 = [1,2,3,4,5]
>>> list2 = [x + 1 for x in list1]
>>> list2
[2, 3, 4, 5, 6]
# 我们可以用语法来创建集合和字典表,开开脑洞
>>> some_list = [1, 2, 3, 4, 5, 2, 5, 1, 4, 8]
>>> even_set = { x for x in some_list if x % 2 == 0 }
>>> even_set
set([8, 2, 4])
# 其实,我们有更简单的方式创建一个集合:
>>> setlist = {1,2,3,4,5,2,3,4}
>>> setlist
set([1,2,3,4,5])
# 创建字典表
>>> d = { x: x % 2 == 0 for x in range(1, 11) }
>>> d
{1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False, 10: True}
# 利用这个脑洞,你还可以用字典推导翻转字典
>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m
{'d': 4, 'a': 1, 'b': 2, 'c': 3}
>>> {v: k for k, v in m.items()}
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

另外,刚刚提到了,直接省略set 方式的创建集合,它还有一些在这基础之上更犀利的应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> A = {1, 2, 3, 3}
>>> A
set([1, 2, 3])
>>> B = {3, 4, 5, 6, 7}
>>> B
set([3, 4, 5, 6, 7])
>>> A | B
set([1, 2, 3, 4, 5, 6, 7])
>>> A & B
set([3])
>>> A - B
set([1, 2])
>>> B - A
set([4, 5, 6, 7])
>>> A ^ B
set([1, 2, 4, 5, 6, 7])
>>> (A ^ B) == ((A - B) | (B - A))
True

7. Counter 计数器

对于我们来说,数一个东西,是非常常用的,然而这件事又不是程序员喜欢做的事情,我们用 counter 来完成这个操作。他在我们python 内置的库里。

1
2
3
4
5
6
7
8
>>> from collections import Counter
>>> c = Counter('hello world')
>>> c
Counter({'l': 3, 'o': 2, ' ': 1, 'e': 1, 'd': 1, 'h': 1, 'r': 1, 'w': 1})
>>> c.most_common(2)
[('l', 3), ('o', 2)]

8. 双端队列

我们都知道,队列和栈实际上就是对在双端队列的基础上实现的,python可以直接操作双端队列。当然也在内置的库 collections 里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> Q = collections.deque()
>>> Q.append(1)
>>> Q.appendleft(2)
>>> Q.extend([3, 4])
>>> Q.extendleft([5, 6])
>>> Q
deque([6, 5, 2, 1, 3, 4])
>>> Q.pop()
4
>>> Q.popleft()
6
>>> Q
deque([5, 2, 1, 3])
>>> Q.rotate(3)
>>> Q
deque([2, 1, 3, 5])
>>> Q.rotate(-3)
>>> Q
deque([5, 2, 1, 3])

同时,我们还可以在括号里添加 maxlen 来限制双端队列的最大长度。last_three = collections.deque(maxlen=3)

9. 默认词典

一般情况下,空词典它就是空的,但是我们利用 collections 里的函数,可以实现默认的字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> m = dict()
>>> m['a']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'a'
# 你可以在括号里添加各种条件
>>> m = collections.defaultdict(int)
>>> m['a']
0
>>> m['b']
0
>>> m = collections.defaultdict(str)
>>> m['a']
''
>>> m['b'] += 'a'
>>> m['b']
'a'
>>> m = collections.defaultdict(lambda: '[default value]')
>>> m['a']
'[default value]'
>>> m['b']
'[default value]'

10. 利用json库打印出漂亮的JSON串

这个方法就是为了让让人面对眼花缭乱的JSON串,能够打印出一个漂亮的可读的格式,对于在控制台交互编程,或者是做日志是,还是非常有用的。另外,也可以注意一下pprint 这个模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import json
data = {"status": "OK", "count": 2, "results": [{"age": 27, "name": "Oz", "lactose_intolerant": true}, {"age": 29, "name": "Joe", "lactose_intolerant": false}]}
>>> print(json.dumps(data)) # No indention
{"status": "OK", "count": 2, "results": [{"age": 27, "name": "Oz", "lactose_intolerant": true}, {"age": 29, "name": "Joe", "lactose_intolerant": false}]}
>>> print(json.dumps(data, indent=2)) {
"status": "OK",
"count": 2,
"results": [
{
"age": 27,
"name": "Oz",
"lactose_intolerant": true
},
{
"age": 29,
"name": "Joe",
"lactose_intolerant": false
}
]
}

11. 最大和最小的几个列表元素

这个经常用到啊,少年们。

1
2
3
4
5
6
import random, heapq
a = [random.randint(0, 100) for __ in xrange(100)]
b = heapq.nsmallest(5, a)
c = heapq.nlargest(5, a)
print b,c
[1, 2, 3, 5, 7] [100, 100, 100, 99, 98]

12. 一些更贴近大脑的写法,和一些掉了下巴的代码段

有一些语句,写出来你就能读懂,就像读一篇文章一样。有时候,其他语言用了超长的代码写出来的程序,python只需要几行,甚至是,1行。

  1. 数值比较
1
2
3
4
5
6
7
x = 2
if 3 > x > 1:
print x
>>> 2
if 1 < x > 0:
print x
>>> 2
  1. 有这么一个算法题,打印数字1到100,3的倍数打印“Fizz”来替换这个数,5的倍数打印“Buzz”,对于既是3的倍数又是5的倍数的数字打印“FizzBuzz”。对此,我们只使用一行代码,搞定它.
1
for x in range(1, 101):print"fizz"[x % 3*4::]+"buzz"[x % 5*4::]or x

13. 一个超小型的Web服务

我们在两台机器或者服务器之间做一些简单的基础的RPC之类的交互,我们就可以用到python 这个神奇的模块。
服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
from SimpleXMLRPCServer import SimpleXMLRPCServer
def file_reader(file_name):
with open(file_name, 'r') as f:
return f.read()
server = SimpleXMLRPCServer(('localhost', 8000))
server.register_introspection_functions()
server.register_function(file_reader)
server.serve_forever()

客户端:

1
2
3
4
import xmlrpclib
proxy = xmlrpclib.ServerProxy('http://localhost:8000/')
proxy.file_reader('/tmp/secret.txt')

这样就得到了一个远程文件读取工具,超小型,没有外部依赖,当然没有任何安全可言,仅作家里使用,当然我现在还没用过这个。


人生苦短,我用Python。

ps. 似乎不说一句这个名言,就不像是在写Python一样。当然了,我还是要滚回去好好地学C++。

script>