(Python3) – 调试&序列化

1.调试:

  • 错误处理:

可以使用try ...  except ... finally .... 语句组合,写出一个错误处理。except ZeroDivisionError as obj : 将错误作为一个对象处理。如果要捕获所有的错误,就可以捕获公有的错误基类BaseException。可以在except所有错误类型后添加else,表示没有错误就会执行的语句。

# coding=utf8
# debug segmentation usage
try:
    print('try...')
    jk = len(2)
    print('rest...')
except ZeroDivisionError as e:
    print('ZeroDivision...', e)
except ValueError as e:
    print('Value exception...')
except BaseException as e:
    print('standard error...', e.__class__)  # catch all potential erro and print the erorr's type
else:
    print('no errors...')
finally:
    print('finally...')
print('END')

更多的错误类型:https://docs.python.org/3/library/exceptions.html#exception-hierarchy

  • 消息日志:logging模块

logging模块定义的日志级别:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET。注意,只会记录不低于指定级别的日志信息。

1.输出指定级别的信息:

logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')

2.输出到文件&stderr:

# coding=gb2312
# 调用堆栈
# Ref : http://www.cnblogs.com/dkblog/archive/2011/08/26/2155018.html
import logging  # 导入日志记录模块
import os,sys

# 记录到日志的文件
logging.basicConfig(level=logging.INFO,  # 级别
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  # 格式
                    datefmt='%a, %d %b %Y %H:%M:%S',  # 日期格式
                    filename=os.getcwd()+os.path.sep.join((os.path.sep, 'debug', 'myapp.log')),  # 文件路径,我这里是保存在项目的莫i一个子目录
                    filemode='w')  # 读写的参数,与open的flag无异

# 输出到错误流
console = logging.StreamHandler()  # 流
console.setLevel(logging.INFO)  # 级别
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)  # 设置为一个指定的格式的对象
logging.getLogger('').addHandler(console)  # 返回一个根logger对象,然后添加一个记录日志的子对象(getLoger的参数为'')
# default path is cwd path

format参数的格式与内容:

 %(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(message)s: 打印日志信息

datefmt: 指定时间格式,同time.strftime()
level: 设置日志级别,默认为logging.WARNING
stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

还有还能丰富的StreamHandler和Log的的配置文件。不过还没有考虑使用。可以参考,文末的连接:)

  • 错误抛出:错误同样是可以再抛出(raise)一个错误的实例,会沿着调用栈向上查找出错的处理代码。
  • 断言:assert exp, str,当exp为假的时候,会抛出一个AssertionError的错误,包含消息str。使用命令行参数-O(大写)可以让Python关闭断言。
  • logging模块也可以抛出错误:记录的错误级别去决议实例e
def foo(s):
    return 10 / int(s)
def bar(s):
    return foo(s) * 2
def main():
    try:
        bar('0')
    except Exception as e:
        logging.exception(e)


logging.debug('DEBUG MESSAGES')
logging.info('INFO MESSAGES')
main()
print('END')
  • pdb调试(pdb模块):可以通过添加参数 -m pdb进入pdb调试。

l:查看当前执行的代码

n:单步执行代码

p 变量名:查看变量

q:结束调试

c:继续执行

pdb插入断点:在需要插入断点的语句的下方插入pdb.set_trace()

  • 单元测试(unittest模块):对一个模块、函数或类进行正确性检验
# coding=gb2312
# 单元测试2
import unittest


class People(object):  # 这个是测试的类

    def __init__(self, name, age):  # 需要测试的初始化函数
        self._name = name
        self._age = age

    def __getattr__(self, key):  # 检查是否存在属性
        if key is not dir(self):
            raise AttributeError('No attribute : %s' % key)
        return key


class Test(unittest.TestCase):  #单元测试的类
    def test_init(self):  # test_开头,表示一个测试的函数 后面的内容是需要测试方法名
        p = People('Shu', 19)
        self.assertEqual(p._name, 'Shu')  # 断言 是否相等
        self.assertEqual(p._age, 19)
        print('OK???')

    def setUp(self):  # 这个是开始测试前的执行的语句
        print('setup...')

    def tearDown(self):  # 结束测试的执行语句
        print('teardown...')


if __name__ == '__main__':  # 判断是否是以非模块形式运行
    unittest.main()  # 调用测试

Test类,还有很多的断言。如果没有问题,就会输出:

Ran 5 tests in 0.000s
OK
  • 文档测试(doctest模块):判断输出是否与交互模式一致。
# coding=gb2312
# 文档测试

def mul(x, *more):
    '''
    >>> mul(1, 2, 3)
    6
    >>> mul(2, 2, 2, 2, 2)
    31
    '''
    pro = x
    for k in more:
        pro *= k
    return pro


if __name__ == '__main__':
    import doctest
    doctest.testmod()

注意:有两个地方可以放doctest测试用例,一个位置是模块的最开头,另一个位置是函数声明语句的下一行(就像上面的例子这样)。除此之外的其它地方不能放,放了也不会执行。

>>:是输入,没有的话就是应该的输出。

例子会输出:

**********************************************************************
File "e:\Python\debug\doctest1.py", line 9, in __main__.mul
Failed example:
   mul(2, 2, 2, 2, 2)
Expected:
    31
Got:
    32
**********************************************************************
1 items had failures:
   1 of   2 in __main__.mul
***Test Failed*** 1 failures.
  • 作为模块启动,可以在python的启动参数后加上-m。区别:

直接启动是把xxx.py文件所在的目录放到了sys.path属性中。__name__的值为“__main__”
模块启动是把你输入命令的目录(也就是当前路径),放到了sys.path属性中。''

2.IO入门:

各种流对象:(io库)

StringIO:类似于C++中的sstream,是保存在内存中的可以读写str。getvalue方法可以保存到字符串的对象。

BytesIO:可以按照字节读取/写入数据。

>>> from io import BytesIO
>>> f = BytesIO()  # 也可以从字节数据初始化BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.write('中文'.encode('utf-8')) # 可以返回写入的字符个数
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'  # 注意写入的是编码的数据(1个字节为单位)

注意:具有读写功能的流,进行了某个一个的读或写操作。可能需要进行seek和tell(似乎没有单独地维护读写的位置)来设置/读取的偏移位置。当读写到流的末尾,read返回空‘’。

3.目录&文件:

参考我的一篇文章:文件操作

4.序列化:

序列化可以方便地传输文件信息,也可以使用一个统一地格式共享程序的数据。

pickling(序列化):变量从内存中变成可存储或传输的过程称之为序列化

unpickling(反序列化):把变量内容从序列化的对象重新读到内存里称之为反序列化

JSON:

JSON类型 Python类型
{} dict
[] list
"string" str
1234.56 int或float
true/false True/False
null None
# json版本的存储
import json
rdr = open('pickling/json_read.json', 'r')  # 从文件中打开JSON文件
reader = json.load(rdr)  # 从json文件中读取
print(type(reader), '\n', reader)
# 写入到其他的json文件
rdr.close()
writer = open('pickling/json_store.json', 'a')
json.dump(reader, writer)

# json.dump(json.loads('{"Country":"China"}'), writer)
writer.close()

json.load():可以从文件流对象载入格式化的数据

json.loads():可以从file-like Object(例如str)读取格式化数据。

json.dump():写入格式化数据到文件流对象。

json.dumps():写入格式化数据到file-like Object(例如str)中。

>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{'age': 20, 'score': 88, 'name': 'Bob'}

将对象序列化:

# coding=gb2312
# json 的高级使用
import json


class student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score


def student2json(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }


def json2student(d):
    return student(d['name'], d['age'], d['score'])


s = student(name='Fang', age=25, score=99)
di = json.dumps(s, default=student2json)
s1 = json.loads(di, object_hook=json2student)
print(type(s1))

5.其他:

  • 上下文管理器:
with open('pickling\pickle.txt', 'rb') as e:
    xxxxx

可以避免某些对象没有正确地释放或者关闭(如:文件流对象因抛出错误关闭),也可以避免使用try ... except ... 造成的代码冗长。

参考