摘录:https://www.liaoxuefeng.com/wiki/1016959663602400/1017598873256736

  • 错误处理
  • 调试
  • 新增:2020-01-19 增加关于文件读取的方法tell(), seek()等知识。

错误处理

高级语言都会使用内置的一套try...except...finally...的错误处理机制, 可以更高效的处理错误,

无需程序员自己写错误处理的代码。

try

try:
print('try...')
r = 10 / int('')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
  • 如果有错误,根据发生不同类型的错误,使用不同的except处理。

    • int('a')会出发 ValueError
    • 10/0会触发ZeroDivisionError。
    • 如果没有找到expect匹配的异常,则将异常传递到外部的try语句中。会显示traceback
  • 否则代表没有错误,则执行else。
  • 无论是否发生异常,最后都要执行finally

常见的错误类型和继承关系

看文档Built-in Exceptions

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

try的好处

可以处理try子句中调用(间接调用)的函数内部发生的异常,即跨多层调用。

函数main()调用bar(), bar调用foo(), 只要期间发生错误,try就会处理。

def foo(s):
return 10 / int(s) def bar(s):
return foo(s) * 2 def main():
try:
bar('')
except Exception as e:
print('Error:', e)
finally:
print('finally...')

调用栈

如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序终止。

错误信息是Traceback (most recent call last)...。它是一个错误路径。可以在最后查看错误原因,定位错误位置。

例子:

import sys

try:
f = open('example.log', 'r+')
s = f.readline()
i = int(s.stripd())
except OSError as err:
print("OS error: {}".format(err))
except ValueError:
print("Could not convert datga to a integer")
except:
print("Unexpected error", sys.exc_info()[0])
raise
else:
print(i) # OSError类, 就是IOError的别名。输入输出错误类.会升起和系统错误代码香港的错误。包括很多子类 # 最后的excepte没有指定异常类,当作通配符用。
# sys.exc_info会返回,当前正在处理的exception的信息:
# 返回的值是一个tuple:包括3个value。(type, value, traceback), 如果没有对应的值返回None。
#example.log
213
1

解释:

  • f = open(), 使用内建方法open。open其实是io模块的方法。
  • s = f.readline(), 会把example.log文件的第一行代码返回给变量s。
  • s.stripd(),这是❌的拼写,多写了一个字面d,系统有内置函数strip(),所以要去掉字母d,否则会执行exept子句。
  • 代码最后一个except子句没有异常类,所以向上级抛错误。
  • sys.exc_info():这个方法是sys模块的方法。用法见注释。

本例子返回的是:

Unexpected error <class 'AttributeError'>
Traceback (most recent call last):
File "linshi.py", line 6, in <module>
i = int(s.stripd())
AttributeError: 'str' object has no attribute 'stripd'

except子句的as

except Exception as inst:

as 后面的inst变量和产生的异常类的实例绑定,如果异常实例有参数,使用inst.args查看。

因为异常类BaseException类定义了object.__str__(self)方法, 这个方法返回对象自身的字符串格式,所以可以直接使用inst,查看错误。

例子:

def this_fails():
x = 1/0 try:
this_fails()
except ZeroDivisionError as err:
print("Handing run-time error:", err.args)
print("Handing run-time error:", err)
#返回:
Handing run-time error: ('division by zero',)
Handing run-time error: division by zero

logging模块

可以把错误信息记录到日志。并让程序继续执行。

# err_logging.py

import logging

def foo(s):
return 10 / int(s) def bar(s):
return foo(s) * 2 def main():
try:
bar('')
except Exception as e:
logging.exception(e) main()
print('END')

抛出异常 raise语句

raise(参数), 参数是异常类或者它的实例。如果参数是异常类,它会通过调用构造函数来暗中/隐式实例化。

内置函数有各种类型的错误。也可以自己编写函数,然后抛出错误。

# err_raise.py
class FooError(ValueError):
pass def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n foo('')
  • 编写一个错误类FooError。
  • 用raise语句,生成FooError的实例。

尽量使用内置的函数。如ValueError, TypeError

最常用的错误处理方式:try...except...并调用一个单独的raise。

目的:

  • 用except捕获指定❌
  • 然后,用raise语句进行trackback。

下面代码:注释掉了raise,所以最后结果不会显示Traceback (most recent call last)的信息。

from functools import reduce

def str2num(s):
return int(s) #改为float(s)即可纠正错误 def calc(exp):
ss = exp.split('+')
ns = map(str2num, ss)
return reduce(lambda acc, x: acc + x, ns) #functools的方法reduce(function, iterable) def main():
try:
r = calc('99 + 88 + 7.6')
print('99 + 88 + 7.6 =', r)
except ValueError as e:
print(">>>%s" % e)
# raise
main()
  • 加上单独的raise的作用,就是把当前错误原样抛出,可进行后续的追踪。
  • 由于当前函数不知道应该怎么处理该错误,所以,继续往上抛错误,让顶层调用者去处理。
  • 如果raise带了不同的异常类实例的参数,,可以把一种类型的错误转化为另一种类型:
try:
10 / 0
except ZeroDivisionError:
raise ValueError('input error!')

finally的作用

finally是try语句的可选子句,它不论是否产生异常都会执行。用于定义清理操作。

但有几点特殊情况,执行的时间不一样。具体可见文档: 8.6. 定义清理操作


调试

第一种方法简单直接粗暴有效,就是用print()把可能有问题的变量打印出来看看。

第二种:print()但看完还要删除,因此可以用assert。

不用了,可以在启动时,带上参数-O关掉assert的功能。

#err.py
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n def main():
foo('')
main()
$ python -O err.py

注意是大写的字母O,

第三种logging。

import logging
logging.basicConfig(level=logging.INFO) #进行配置,logging级别,默认是WARNING,所以INFO,DEBUG级别的不会被追踪 s = ''
n = int(s)
logging.info('n= %d' % n)
print(10/0)

这就是logging的好处,它允许你指定记录信息的级别。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。

级别

何时使用

DEBUG

细节信息,仅当诊断问题时适用。

INFO

确认程序按预期运行

WARNING

表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行

ERROR

由于严重的问题,程序的某些功能已经不能正常执行

CRITICAL

严重的错误,表明程序已不能继续执行

默认的级别是``WARNING``,意味着只会追踪该级别及以上的事件,除非更改日志配置。

追踪的事件可以:

  • 简单的:输出到控制台
  • 复杂的:写入磁盘文件。

记录日志到磁盘文件:

使用basicConfig()的配置函数,更改上面的代码:

logging.basicConfig(filename='example.log',level=logging.DEBUG)

这样,会在当前文件夹下生成一个example.log文件,用于记录日志信息。就是logging.info()输出的信息。

在消息中显示时间/日期

日志很重要的是,可以在之后查看,所以要加上日期时间:

logging.basicConfig(filename='example.log',format="%(levelname)s: %(asctime)s %(message)s",level=logging.INFO)  #进行配置

加上参数:format="%(levelname)s: %(asctime)s %(message)s"

会在日志显示:

WARNING: 2019-11-16 19:29:46,496 n= 0

是否要深入学习?

如果日志需求很简单,上面的就足够了,否则可以看进阶日志教程和操作手册。

第四种pdb, Python自带的调试器

$ python -m pdb err.py
> /Users/michael/Github/learn-python3/samples/debug/err.py(2)<module>()
-> s = ''

带上参数-m pdb

运行时,c是continue下一个块, n是next即下一行,q是退出。

如果想要设置一个中断点,在代码中加上pdb.set_trace()方法即可,运行是到这行代码会暂停并进入调试程序。

⚠️类似Ruby/Rails的byebug。中断点是byebug, 启动脚本: byebug xxx.rb

IDE: 集成开发环境

如果要比较爽地设置断点、单步执行,就需要一个支持调试功能的IDE。目前比较好的Python IDE有:

Visual Studio Code:https://code.visualstudio.com/,需要安装Python插件。

PyCharm:http://www.jetbrains.com/pycharm/

另外,Eclipse加上pydev插件也可以调试Python程序。

虽然用IDE调试起来比较方便,但是最后你会发现,logging才是终极武器。


文件的定位读写

f = open("123.txt", "w+")
f.write("hello world")
content = f.read()
print(content) # 为什么打印不出内容?

这是因为有一个关于打印指针的概念。

第3行的变量content其实是""空字符串,因为文件中的指针当前指向的是文本的最后。

所以,如果希望打印第2行输入的字符,需要调整文件指针的位置。这里涉及2个方法:

  • tell(),判断当前指针的位置
  • seek(offset, from) 移动指针

seek(offset, from)有2个参数

  • offset:偏移量。正整数向右偏移,负数向左偏移。
  • from:方向
    • 0:表示文件开头
    • 1:表示指针当前的位置
    • 2:表示文件末尾

最新文章

  1. Python之路【第六篇】python基础 之面向对象进阶
  2. HTML中的图像
  3. Ext.NET 4.1.0 搭建页面布局
  4. ASP.Net页面刷新后自动滚动到原来位置
  5. ArcGIS Server发布服务,报错001270
  6. This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms. 此实现不是 Windows 平台 FIPS 验证的加密算法的一部分 解决方案
  7. java中print\println\printf的区别
  8. hdu 4676 Sum Of Gcd
  9. 可变参数列表---以dbg()为例
  10. MVC 文本转换成html显示
  11. oracle用户创建,连接,删除
  12. kubernetes入门(09)kubernetes的命令
  13. SQL中GROUP BY用法示例(转)
  14. 报表工具-ECharts 特性介绍
  15. YUV介绍
  16. 2016.3.24 OneZero站立会议
  17. Python MRO_C3
  18. Landen邀请码
  19. numpy的生成网格矩阵 meshgrid()
  20. js es6 map 与 原生对象区别

热门文章

  1. RNN汇总
  2. Kafka Connector无法启动的原因
  3. Linux远程开发
  4. Linux用户管理命令介绍
  5. Plsql配置后,sql语句可以简写 快速使用
  6. 使用Minikube运行一个本地单节点Kubernetes集群(阿里云)
  7. PHP会话(Session)实现用户登陆功能
  8. Vue 表情输入组件,微信face表情组件
  9. windows服务总结
  10. JS基础_算数运算符