#错误、调试和测试
##错误
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
第二个except永远也捕获不到UnicodeError,因为UnicodeError是ValueError的子类,已经被第一个except捕获。所有的错误类型都基于BasEexception。
Traceback (most recent call last):#错误跟踪信息
File "err.py", line 11, in <module>
main()#main()出错,对应第11行,原因却在第9行
File "err.py", line 9, in main
bar('0')
File "err.py", line 6, in bar
return foo(s) * 2
File "err.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
这个是调用栈,如果错误没找到,则一直向上传递。
用logging函数记录错误信息。
用raise函数抛出错误。
Traceback (most recent call last):
File "err_throw.py", line 11, in <module>
foo('0')
File "err_throw.py", line 8, in foo
raise FooError('invalid value: %s' % s)#raise函数展示了我们自己定义的错误类型
__main__.FooError: invalid value: 0#raise抛出了我们自己定义的错误类型
##调试
在找bug时需要打印错误信息,用assert,最后调用-O即可。
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
但是升级版的logging更好,它可以把错误传向文件。
import logging
logging.basicConfig(level=logging.INFO)
加上第二行语句就可以把错误传向文件十分方便。可以迅速找到文件错误的位置。
##单元测试
单元测试就是对某一类进行测试,如果将来把这个类修改了,只需要调用单元测试来判断这个类有没有错误。
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)#断言输出是不是我们所期望的
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
引入python自带的unittest进行测试,用assertEqual断言,还可以返回出错误类型等等我们所期望的。
if __name__ == '__main__':
unittest.main()
调用最后两行代码执行单元测试。
#IO编程
##StringIO和BytesIO
二进制写入文件
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))#写入的不是字符串
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
读取二进制的文件
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87
##操作文件和目录
posix对应windows系统
>>> os.environ.get('PATH')
'/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin'
获取某个环境变量的值
# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'/Users/michael'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('/Users/michael', 'testdir')
'/Users/michael/testdir'
# 然后创建一个目录:
>>> os.mkdir('/Users/michael/testdir')
# 删掉一个目录:
>>> os.rmdir('/Users/michael/testdir')
#合并路径:
os.path.join()
#拆分路径:
os.path.split()
#直接得到文件扩展名:
os.path.splitext()
#列出当前目录下所有目录:
>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]
#列出所有py文件:
>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']
##序列化
我们把变量从内存中变成可存储或传输的过程称之为序列化,pickling,反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
具体操作如下:
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)#用这个函数把它序列化
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)#用这个函数把对象序列化后写入file-like Object
>>> f.close()
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)#用这个函数把序列化的内容转回来
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
#进程和线程
##多进程multiprocessing
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
如果创建大量的子进程,需要用进程池的方法。
import subprocess
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])#在python代码中运行这个命令
print('Exit code:', r)
进程间通信是通过Queue、Pipes等实现的。
##多线程
多任务可以由多进程完成,也可以由一个进程内的多线程完成。
一个简单的运算balance = balance + n在CPU中也可以分成两步,因此经常会出错。
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
用threading.Lock()可以有效解决这个问题。
分布式进程:把任务发送到多台计算机上执行。