Writing Solid Python Code

编写高质量代码(Python)

改善Python程序的91个建议


编辑时间:2018.9.14

本文为原书的摘抄,原书中有些问题在后来Python的新版本中已经得到改善,所以本文中出现与原书不同之处为我自己测试样例后改动的,重在学习思想即可。本文具有一定的时限性,我是针对于Python3.6.6做的样例测试。原书中某些内容本文中没有出现的原因是太简单、本人觉得不妥或者上一篇Effective Python中有,另外原书中也有一些错误,但不妨碍它成为一本好书。


引论

  1. ~
    • 避免只用大小写来区分不同的对象
    • 避免使用容易引起混淆的名称
    • 不要害怕过长的变量名
  2. ~
    • 三元操作符?:
      • C?X:Y等价于X if C else Y

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if x == 1:
print('1')
elif x == 2:
print('2')
else:
print('None')


# 改善后的代码
def search(x):
return {
1: '1,',
2: '2',
}.get(x, 'None')

  1. ~
    • 给外部可访问的函数和方法添加文档注释(描述方法的功能,输入输出)
    • copyright声明、模块描述等(作者、变更描述)
    • 注释应该是用来解释代码的功能、原因及想法的,而不是对代码本身的解释
  2. ~
    • 在一组代码表达完一个完整的思路后,应该用空白行进行间隔
    • 避免过长的代码行,每行最好不超过80个字符
    • 空格
      • 二元操作符的左右应该有空格,比较(==、<、>、!=、<>、<=、>=、in、not in、is、is not)、布尔运算(and、or、not)
      • 逗号和分号前不要使用空格
  3. ~
    • 将常量集中到一个文件(整个项目的全局变量)、加载文件引用全局变量

编程惯用法

  1. ~
    • 利用assert语句发现问题

示例:

1
2
3
4
5
6
7
8
9
10
11
12
a = 1
b = 2

assert a == b, 'this is False'


# result
#
# Traceback (most recent call last):
# File "test.py", line 4, in <module>
# assert a == b, 'this is False'
# AssertionError: this is False

  1. ~
    • 数据值交换的时候不推荐使用中间变量

示例:

1
2
# 交换a、b的值
a, b = b, a

  1. ~
    • Lazy evaluation,惰性计算或者延迟计算,指的是仅仅在真正需要执行的时候才计算表达式的值
      • 避免不必要的计算,带来计算性能上的提升
        • if x and y,如果x为False应当直接返回而不是继续计算y
        • if x or y,当x为True使直接返回而不是计算y的值
      • 节省空间,使得无限循环的数据结构称为可能
        • 生成器表达式yield
  2. ~
    • 使用类属性

示例:

1
2
3
4
5
6
7
8
9
class age:
mary = 0
allen = 1
james = 2


# 简化后
class age:
mary, allen, james = range(3)

  1. ~
    • 不建议使用type进行类型检查
      • type()
        • 判断一个变量是不是list类型使用代码:if type(a) is types.ListType:
        • 例:
          • types.BooleanType
          • types.IntType
          • types.StringType
          • types.DictType
        • 基于用户内建类型扩展的用户自定义类型,type函数并不能准确返回结果
      • 建议使用isinstance()函数来检测
        • isinstance(object, classinfo)

示例:

1
2
3
4
>>> isinstance(2, float)
False
>>> isinstance((2, 3), (str, list, tuple)) # 支持多种类型列表
True

  1. ~
    • 尽量转换为浮点数后再去做出发
  2. ~
    • 警惕eval()的安全漏洞
      • eval语法
        • eval(expression[, globals[, locals]])
        • expression – 表达式。
        • globals – 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
        • locals – 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。

示例:

1
2
3
4
5
6
7
8
9
10
>>>x = 7
>>> eval( '3 * x' )
21
>>> eval('pow(2,2)')
4
>>> eval('2 + 2')
4
>>> n=81
>>> eval("n + 4")
85

  1. ~
    • 使用enumerate()函数序列迭代的索引和值
  • 方法一
1
2
3
4
5
6
7
8
9
10
11
12
list_ = ['a', 'b', 'c']
index = 0
for i in list_:
print('index:', index, 'element:', i)
index += 1


# result
#
# index: 0 element: a
# index: 1 element: b
# index: 2 element: c
  • 方法二
1
2
3
4
5
6
7
8
9
10
list_ = ['a', 'b', 'c']
for i in range(len(list_)):
print('index:', i, 'element:', list_[i])


# result
#
# index: 0 element: a
# index: 1 element: b
# index: 2 element: c
  • 方法三
1
2
3
4
5
6
7
8
9
10
11
12
list_ = ['a', 'b', 'c']
index = 0
while index < len(list_):
print('index:', index, 'element:', list_[index])
index += 1


# result
#
# index: 0 element: a
# index: 1 element: b
# index: 2 element: c
  • 方法四
1
2
3
4
5
6
7
8
9
10
list_ = ['a', 'b', 'c']
for i, e in zip(range(len(list_)), list_):
print('index:', i, 'element:', e)


# result
#
# index: 0 element: a
# index: 1 element: b
# index: 2 element: c
  • 方法五
1
2
3
4
5
6
7
8
9
10
list_ = ['a', 'b', 'c']
for i, e in enumerate(list_):
print('index:', i, 'element:', e)


# result
#
# index: 0 element: a
# index: 1 element: b
# index: 2 element: c
  1. ~
    • 分清==与is的适用场景
      • is表示的是对象标示符,不仅地址要相同而且值要相同
      • ==表示的是意思相等,只需要值相同就好而不需要地址相同

示例:

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 = 'hello'
b = 'hello'
print(a is b)
print(a == b)
print('id:', id(a), 'value:', a)
print('id:', id(b), 'value:', b)

c = 'hello world'
d = 'hello ' + 'world'
print(c is d)
print(c == d)
print('id:', id(c), 'value:', c)
print('id:', id(d), 'value:', d)


# result
#
# True
# True
# id: 2776211607480 value: hello
# id: 2776211607480 value: hello
# False
# True
# id: 2776212605936 value: hello world
# id: 2776212684272 value: hello world

  1. ~

    • 考虑兼容性,注意编码格式
      • unicode
      • utf-8
        • 使用utf-8格式的话,要在程序文件头部加上:
          • -*- coding:utf-8 -8-
  2. ~

    • 构建合理的包层次来管理module
      • 使用多级文件来封装模块,分级使用和调用模块以及所在层内的全局变量
      • 上层模块对自身的下一层模块跨文件互调

示例:

1
2
3
4
5
6
7
8
Module/
__init__.py
module_1.py
module_2.py
SubPackage/
__init__.py
module_1.py
module_2.py

基础语法

  1. ~

    • 有节制使用from···import语句
      • 原因
        • 因为调用其他模块的时候必然会有文件读写操作,这个效率是比较低的
      • 尽量优先使用import ···,访问对象时使用···.···
      • 有节制使用from ··· import ···
      • 避免使用from ··· import *,这回污染命名空间,并且无法清晰地表示导入了哪个对象
  2. ~

    • i++不等于++i
1
2
3
4
5
6
7
8
9
10
11
12
print(++1)
print(--1)

i = 1
i += 1
print(i)


# result
# 1
# 1
# 2
  1. ~
    • 使用with自动关闭资源
1
2
3
4
5
6
7
8
9
10
11
f = open(path, 'w')
f.write(opterate)

# 往往会只集中于操作上,忘记关闭文件

# with 表达式 [as 目标]:
# 代码块
with open(path, 'w') as f:
f.write(opterate)

# 代码块的操作执行完毕后,会自动关闭文件
  1. ~
    • 异常处理
1
2
3
4
5
6
try:
operate
except Exception as err:
return err

# 这里执行try,捕捉的错误是python中定义的所有错误中的任意一种(你也可以直接定义是某类错误,这里是当你不确定是那种错误的时候)
  1. ~

    • 避免finally中可能发生的异常
      • 无论try语句中是否有异常抛出,finally语句总会被执行
  2. ~

    • 深入理解Nnoe,正确判断对象为空
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
27
28
29
30
print(id(None))

print(None == 0) # None不为0

print(None == '') # None不等于空字符串

a = None
b = None
print(a == b) # 所有的None对象都相同
print(id(a), id(b))

list_ = []
print(list_, list_ is None) # 空的list对象不为空

tuple_ = ()
print(tuple_, tuple_ is None) # 空的tuple对象不为空

dict_ = []
print(dict_, dict_ is None) # 空的dict对象不为空


# result
# 1479058576
# False
# False
# True
# 1479058576 1479058576
# [] False
# () False
# [] False
  1. ~
    • 链接字符串优先使用join为不是+
      • +每次相加都要申请新的内存,n个字符串相加就要申请n-1个内存
      • 使用join时,会事先计算需要多少内存,然后只申请一次
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import time


a = 'hello '
b = 'world '
c = '!'

# 短连接

time_1 = time.time()

for i in range(1000000):
d = a + b + c
time_2 = time.time()

for i in range(1000000):
d = ''.join([a, b, c])
time_3 = time.time()

print('+\t:', str(time_2-time_1))
print('.join\t:', str(time_3-time_2))

# 长连接

time_4 = time.time()

e = ''
for i in range(1000000):
e = e + (a + b + c)

time_5 = time.time()

temp_list = ['hello world !' for j in range(1000000)]
e = ''.join(temp_list)

time_6 = time.time()

print('+\t:', str(time_5-time_4))
print('.join\t:', str(time_6-time_5))

# result
# + : 0.15621352195739746
# .join : 0.21873021125793457
# + : 8.51358938217163
# .join : 0.062482595443725586

这里的测试可以看出,join在短字符串拼接速度未必有+快,毕竟join是Python的解释器封装了一层。当长字符串拼接的时候二者相差速度很大,个人要根据情况使用。

  1. ~
    • 格式化字符串尽量使用.format方式而不是%
      • 理由:
        • .format方式更加灵活,参数顺序没有要求
        • 方便参数传递
        • 官方推荐.format,%只是为了向后兼容而保留
      • 性能上我这里没有测试,个人觉得%效率更高,毕竟.format是封装了一层
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
27
28
29
30
31
32
33
34
35
36
37
38
39
# %操作格式化字符串
print('value is %06.1f' % 9.5) # 直接格式化字符

print('a %s c %s' % ('b', 'd')) # 使用元组形式格式化

print('a:%(a)s \t b:%(b)s' % {'a': 1, 'b': 2}) # 使用字典形式格式化

# result
# value is 0009.5
# a b c d
# a:1 b:2

# .format操作格式化字符串
print('a {0} c {1}'.format('b', 'd')) # 使用位置符号
print('1 {0:,} 3 {1}'.format(2, 4))

print('1 {a} 3 {b}'.format(a=2, b=4)) # 使用名称


class People:
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
return 'Information of people is {self.name}\t{self.age}'.format(self=self)


print(People('tom', 18)) # 通过属性

tuple_ = (1, 2)
print('{0[0]}\t{0[1]}'.format(tuple_)) # 使用元组

# result
# a b c d
# 1 2 3 4
# 1 2 3 4
# Information of people is tom 18
# 1 2
  1. ~

    • 函数既不是传参也不是穿引用
      • 可变对象传应用,不可变对象传值
  2. ~

    • 慎用变长参数
      • 使用*args来实现可变参数列表:*args用于接受一个包装为元组形式的参数列表来传递非关键字参数,参数个数可以任意。
      • 使用**kwargs接受字典形式的关键字参数列表,其中字典的键值对分别表示不可变参数的的参数名和值。
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
# *args
def sum_(*args):
result = 0
for x in args[0:]:
result += x
return result


print(sum_(1, 2, 3))


# **kwargs
def print_(**kwargs):
for name, value in kwargs.items():
print('{0}\t{1}'.format(name, value))


print(print_(a=1, b=2, c=3))


# result
# 6
# a 1
# b 2
# c 3
# None
  1. ~
    • str()repr()的区别
      • 作用:
        • str()和repr()都可以将Python对象转换为字符串
      • 区别:
        • str主要是面向用户,其目的的可读性
        • repr是面向解释器,编程人员debug用途,repr可以用eval()函数还原对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = 'hello\tworld'
print(str(s))
print(repr(s))
print(eval(repr(s)), end='\t')
print(eval(repr(s)) == s)
try:
print(eval(str(s)), end='\t')
except Exception as err:
print(err)


# result
# hello world
# 'hello\tworld'
# hello world True
# unexpected EOF while parsing (<string>, line 1)
  1. ~
    • 分清staticmethod和classmethod的使用场景
      • 静态方法装饰器下定义的方法属于函数,存放逻辑性代码,主要是一些逻辑属于类,但是和类本身没有交互,即在静态方法中,不会涉及到类中的方法和属性的操作。可以理解为将静态方法存在此类的名称空间中。
      • 类方法装饰器下定义的方法属于方法,类方法是将类本身作为对象进行操作的方法。他和静态方法的区别在于:不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Method:
def print_(self, x):
print(self, x)

@classmethod
def class_print(cls, x):
print(cls, x)

@staticmethod
def static_print(x):
print(x)


if __name__ == '__main__':
Method.print_(None, 'hello')
Method.class_print('hello')
Method.static_print('hello')


# result
# None hello
# <class '__main__.Method'> hello
# hello

  1. ~
    • 按需选择sort()sorted()
      • 使用形式:
        • 表示
          • sorted(iterable[, cmp[, key[, reverse]]])
          • s.sort([cmp[, key[, reverse]]])
        • 解释:
          • cmp为用户定义的比较函数,函数的参数为两个可比较的元素
          • key是带一个参数的函数,用来为每个元素提取比较值
          • reverse表示排序结果是否反转
      • 相比于sort(),sorted()函数使用范围更广
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 对字典排序
dict_ = {
'tom': '14212',
'jury': '41221',
'mark': '21243'
}
from operator import itemgetter
dict_1 = sorted(dict_.items(), key=itemgetter(1))
print(dict_1)

# 多维list排序
list_ = [
['tom', 'male', '14212'],
['jury', 'female', '41221'],
['mark', 'male', '21243']
]
from operator import itemgetter
list_1 = sorted(list_, key=itemgetter(2, 1))
print(list_1)

# 字典中混合list排序
dict_list_ = {
'tom': ['male', 14212],
'jury': ['female', 41211],
'mark': ['male', 21243]
}
from operator import itemgetter
dict_list_1 = sorted(dict_list_.items(), key=lambda v: itemgetter(0)(v))
print(dict_list_1)

# list中混合字典排序
list_dict_ = [
{'name': 'bob', 'age': 10},
{'name': 'david', 'age': 3},
{'name': 'patty', 'age': 4},
{'name': 'carol', 'age': 9}
]
from operator import itemgetter
list_dict_1 = sorted(list_dict_, key=itemgetter('age'))
print(list_dict_1)


# result
# [('tom', '14212'), ('mark', '21243'), ('jury', '41221')]
# [['tom', 'male', '14212'], ['mark', 'male', '21243'], ['jury', 'female', '41221']]
# [('jury', ['female', 41211]), ('mark', ['male', 21243]), ('tom', ['male', 14212])]
# [{'name': 'david', 'age': 3}, {'name': 'patty', 'age': 4}, {'name': 'carol', 'age': 9}, {'name': 'bob', 'age': 10}]
  1. ~
    • 使用copy模块深拷贝对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*-coding:utf-8 -*-
import copy


a = [1, 2, 3, 4, ['a', 'b']] # 原始对象

b = a # 赋值,传对象的引用
c = copy.copy(a) # 对象拷贝,浅拷贝
d = copy.deepcopy(a) # 对象拷贝,深拷贝

a.append(5) # 修改对象a
a[4].append('c') # 修改对象a中的['a', 'b']数组对象

print('a = ', a)
print('b = ', b)
print('c = ', c)
print('d = ', d)


# result
# a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
# b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
# c = [1, 2, 3, 4, ['a', 'b', 'c']]
# d = [1, 2, 3, 4, ['a', 'b']]
  1. ~

    • 使用pandas处理大型csv文件
  2. ~

    • 使用ElementTree解析XML
      • xml.dom.minidom和xom.sax解析XML文件
  3. ~

    • session和cache存储
    • 序列化,就是把内存中的数据结构在不丢失其身份和类型信息的情况下转换为二进制表示的过程
    • 调用
      • pickle.dump(obj, file[, protocol]):序列化数据到一个文件描述符(一个打开的文件、套接字等)
      • load(file):反序列化
    • 特性:
      • 不能保证操作的原子性
      • 存在安全性问题
      • pickle是Python独有的,不同语言之间可能不兼容
  4. ~

    • 序列化的另一个选择–JSON
  5. ~

    • 使用traceback获取栈信息
  6. ~

    • 使用logging记录日志信息
      • 日志级别:DEBUG, INFO, WARNING, ERROR, CRITICAL
      • 对象:
        • logger:程序在运行的时候记录相应的信息,并根据设置的日志级别或filter来决定哪些信息需要输出
        • Handler:处理信息的输出
        • Formatter:log信息的格式
        • Filter:用来决定哪些信息需要输出
  7. ~

    • 使用threading模块编写多线程程序
      • thread模块提供了多线程底层支持模块,以低级原始的方式来处理和控制线程,使用起来比较复杂
      • threading模块基于thread进行包装,将线程的操作对象化
      • thread模块不支持守护进程
      • Python3已经不存在thread模块
  8. ~

    • 使用Queue使多线程编程更安全
      • Queue.Queue(maxsize):先进先出,maxsize为队列大小,其值为非正数的时候为无限循环队列
      • Queue.LifoQueue(maxsize):后进后出
      • Queue.PriorityQueue(maxsize):优先队列
      • Queue.qsize():返回近似的队列大小
      • Queue.empty():队列判空
      • Queue.full():队列的判满
      • Queue.put(item[, block[, timeout]]):往队列中添加元素tiem,block设置为False的时候,如果队列满则抛出Full异常~~~
      • Queue.put_nowait(item):等价于put(item, False).block设置为False的时候
      • Queue.get([block[, timeout]]):从队列中删除元素并返回该元素的值
      • Queue.get_nowait():等价于get(False)
      • Queue.task_done():发送信号表明入列元素已经完成
      • Queue.join():阻塞直至队列中所有的元素处理完毕

设计模式

内部机制

  1. ~

    • 理解名字查找机制
      • 局部作用域
      • 全局作用域
      • 嵌套作用域
      • 内置作用域
  2. ~

    • 使用更为安全的property
      • 用来实现属性可管理的built-in数据类型
      • property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
      • 代码简洁
      • 控制属性权限访问,提高数据的安全性
      • 代码维护性更好
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class People(object):
def __init__(self):
self.age = 18

@property
def main(self):
return self.age


pl = People()
print(pl.main)
try:
pl.main = 12
print(pl.main)
except Exception as err:
print(err)


# result
# 18
# can't set attribute
  1. ~

    • 掌握metaclass
      • 元类是关于类的类,是类的模板
      • 元类是用来控制创建类的
      • 元类的实例为类
  2. ~

    • 协程,又称微线程
      • 线程缺点:
        • 对队列的操作需要有显式/隐式(使用线程安全的队列)的加锁操作
        • “谦让式”的多进程CPU资源分配方式,性能很低
  3. ~

    • 理解GIL的局限性
      • 多线程Python程序运行的速度比只有一个线程的时候还要慢
      • GIL称为全局解释器锁,是Python虚拟机上用作互斥线程的一种机制,它的作用是保证任何情况下虚拟机中只会有一个线程被运行,其它线程都处于等待GIL锁被释放的状态。
  4. ~

    • 对象的管理与垃圾回收
      • 显式调用gc.collect()进行垃圾回收
      • 创建新的对象为其分配内存的时候,检查threshold阈值,当对象的数量超过threshold的时候便自动进行垃圾回收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 类的内存管理
import gc
print(gc.isenabled())
print(gc.get_threshold())

# 变量的内存管理
a = 1
print(a)
del a
try:
print(a)
except Exception as err:
print('a is already delete')


# result
# True
# (700, 10, 10)
# 1
# a is already delete

使用工具辅助项目开发

  1. ~

    • 做paster创建包
  2. ~

    • 理解单元测试概念
  3. ~

    • 将包发布到PyPI

性能刨析与优化

  1. ~

    • 借助性能优化工具
      • Psyco
      • Pypy
  2. ~

    • 利用cProfile定位性能瓶颈
  3. ~

    • memory_profile
    • Objgraph
  4. ~

    • 充分利用set的优势
      • Python中集合是通过Hash算法实现的无序不重复的元素集
      • 操作
        • s.union(t):s和t的并集
        • s.itersection(t):s和t的交集
        • s.difference(t):s和t的差集
        • s.symmetric_difference(t):s和t的并集减去s和t的交集
1
2
3
4
5
6
7
8
9
10
set_1 = set('hello')
print(set_1)
set_2_ = [1, 2, '34', (5, 6)]
set_2 = set(set_2_)
print(set_2)


# result
# {'h', 'l', 'o', 'e'}
# {(5, 6), 1, 2, '34'}
  1. ~

    • 使用multiprocessing克服GIL的缺陷
  2. ~

    • 使用线程池提高效率
      • 创建、就绪、运行、阻塞和终止
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import Queue  # 队列模块
import sys # 系统调用模块
import threading # 多线程模块
import urllib # 网络访问请求模块
import os # 系统操作模块


# 处理request的工作进程
class Worker(threading.Thread):
def __init__(self, work_queue, result_queue, **kwds):
threading.Thread.__init__(self, **kwds)
self.setDaemon(True)
self.workQueue = work_queue
self.resultQueue = result_queue

def run(self):
while True:
try:
callable, args, kwds = self.workQueue.get(False) # 从一个队列中取出一个任务
res = callable(*args, **kwds)
self.resultQueue.put(res) # 存放处理结果到队列中
except Queue.Empty:
break


class WorkerManager: # 线程池管理器
def __init__(self, num_of_workers=10):
self.work_queue = Queue.Queue() # 请求队列
self.result_queue = Queue.Queue() # 输出结果的队列
self.works = []
self._recuritThreads(num_of_workers)

def _recuritThreads(self, num_of_workers):
for i in range(num_of_workers):
worker = Worker(self.work_queue, self.result_queue)
self.works.append(worker)

def start(self): # 启动线程
for w in self.works:
w.start()

def wait_for_complete(self):
while len(self.works):
worker = self.works.pop() # 从池中取出一个线程处理请求
worker.join()
if worker.isAlive() and not self.work_queue.empty():
self.workers.append(wroker) # 重新加入线程池中
print('All job were completed.')

def add_job(self, callable, *args, **kwds):
self.work_queue.put((callable, args, kwds)) # 往工作队列中加入请求

def get_result(self, *args, **kwds): # 获取处理结果
return self.result_queue.get(*args, **kwds)

def download_file(url):
print('start download', url)
urlhandler = urllib.urlopen(url)
f_name = op.path.base_name(url)+'.html'
with open(f_name, 'wb') as f:
while True:
chunk = urlhandler.read(1024)
if not chunk:
break
f.write(chunk)


urls = ['http://baidu.com',
'http://google.com'
]
wm = WorkerManager(2) # 创建线程池
for i in urls:
wm.add_job(download_file, i) # 将所有请求加入队列中
wm.start()
wm.wait_for_complete()

上边的代码没有测试,慎用

  1. ~

    • 使用C/C++模块扩展提高性能
      • 使用ctype模块调用C语言
      • 使用SWIG调用C代码
  2. ~

    • 使用Cython编写扩展模块
      • Cython对性能并没有成倍的提升
      • Cython就相当于一门新的语言,和python关系不大
      • 不建议使用Cython
      • 安装
        • pip install -U cython
Donate comment here