可以通过try-except-finally
对错误进行捕获和处理
当认为某些代码可能会出错时,就可以用try
来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except
语句块,执行完except
后,如果有finally
语句块,则执行finally
语句块,至此,执行完毕。
try:
a = 1 / 0
# 处理除零错误
except ZeroDivisionError as e:
print(e)
finally:
print('is finally')
'''
division by zero
is finally
'''
处理多个错误
try:
a = 1 / int('a')
# 处理除零错误
except ZeroDivisionError as e:
print(e)
# 处理参数错误
except ValueError as e:
print(e)
finally:
print('is finally')
'''
invalid literal for int() with base 10: 'a'
is finally
'''
可以在except语句块后面添加else语句块,如果没有错误发生,将执行else语句块
try:
a = 1 / int('1')
# 处理除零错误
except ZeroDivisionError as e:
print(e)
# 处理参数错误
except ValueError as e:
print(e)
else:
print('is else')
finally:
print('is finally')
'''
is else
is finally
'''
with 语句是 Python 中用于简化资源管理的语法糖。它确保在进入代码块时自动获取资源,并在退出代码块时自动释放资源。常见的资源包括文件、网络连接、数据库连接等。with 语句的核心思想是“上下文管理”,即在一定范围内自动处理资源的获取和释放,避免了手动管理资源带来的复杂性和潜在错误。
总结起来使用python 提供的with主要的作用是:
实现自动调用对象资源的释放
异常捕获和回滚
减少用户手动调用的方法
常见的几个应用场景有:
1、文件操作。
2、进程线程之间互斥对象。
3、支持上下文其他对象
4、需要进行资源链接和结束的时候进行释放操作
5、释放锁
6、创建代码补丁
with 语句依赖于 上下文管理器(Context Manager),这是一个实现了 __enter__
和 __exit__
方法的对象。__enter__
方法在进入 with
代码块时调用,通常用于获取资源;__exit__
方法在退出 with
代码块时调用,通常用于释放资源。
with
语句的基本语法如下:
with context_manager as variable:
# 执行代码块
其中,context_manager
是一个实现了上下文管理协议的对象,variable
是可选的,用于接收 __enter__
方法返回的值。
with open('example.txt', 'r') as file:
content = file.read()
print(content)
import requests
with requests.Session() as session:
response = session.get('https://api.example.com/data')
print(response.json())
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
for row in rows:
print(row)
import time
class Timer:
def __enter__(self):
self.start_time = time.time()
return self
def __exit__(self, exc_type, exc_value, traceback):
end_time = time.time()
elapsed_time = end_time - self.start_time
print(f"Elapsed time: {elapsed_time:.2f} seconds")
# 使用自定义上下文管理器
with Timer():
time.sleep(2)
with
语句支持同时管理多个上下文管理器,只需将它们用逗号分隔即可。这对于需要同时管理多个资源的场景非常有用。
with open('file1.txt', 'r') as f1, open('file2.txt', 'r') as f2:
content1 = f1.read()
content2 = f2.read()
if content1 == content2:
print("Files are identical")
else:
print("Files are different")
with
语句不仅可以管理资源,还可以捕获和处理异常。__exit__
方法可以接受三个参数:exc_type
、exc_value
和 traceback
,分别表示异常类型、异常值和堆栈跟踪。如果 __exit__
方法返回 True
,则表示异常已被处理,不会传播到外部;如果返回 False
或不返回任何值,则异常会继续传播。
假设我们想在文件读取过程中捕获并处理 FileNotFoundError
异常。我们可以在自定义上下文管理器中实现这一功能。
class FileOpener:
def __init__(self, filename):
self.filename = filename
self.file = None
def __enter__(self):
try:
self.file = open(self.filename, 'r')
return self.file
except FileNotFoundError:
print(f"File {self.filename} not found")
return None
def __exit__(self, exc_type, exc_value, traceback):
if self.file:
self.file.close()
# 使用自定义上下文管理器
with FileOpener('nonexistent.txt') as file:
if file:
content = file.read()
print(content)
else:
print("File not found, skipping...")
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- EncodingWarning
+-- ResourceWarning
如果不捕获错误,自然可以让Python解释器来打印出错误堆栈,但程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。
Python内置的logging
模块可以非常容易地记录错误信息
import logging
try:
a = 1 / 0
except BaseException as e:
print(e)
logging.exception(e)
finally:
print('is finally')
'''
division by zero
ERROR:root:division by zero
Traceback (most recent call last):
File "d:\python_project\demo\main.py", line 4, in <module>
a = 1 / 0
ZeroDivisionError: division by zero
is finally
'''
CRITICAL > ERROR > WARNING > INFO > DEBUG
debug
: 打印全部的日志,详细的信息,通常只出现在诊断问题上
info
: 打印info,warning,error,critical级别的日志,确认一切按预期运行
warning
: 打印warning,error,critical级别的日志,一个迹象表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如。磁盘空间低”),这个软件还能按预期工作
error
: 打印error,critical级别的日志,更严重的问题,软件没能执行一些功能,exception属于error,但是会显示堆栈
critical
: 打印critical级别,一个严重的错误,这表明程序本身可能无法继续运行
Logging.Formatter # 这个类配置了日志的格式,在里面自定义设置日期和时间,输出日志的时候将会按照设置的格式显示内容。
Logging.Logger # Logger是Logging模块的主体,进行以下三项工作:
# 1. 为程序提供记录日志的接口
# 2. 判断日志所处级别,并判断是否要过滤
# 3. 根据其日志级别将该条日志分发给不同handler
# Logger常用函数有:
Logger.setLevel() # 设置日志级别
Logger.addHandler() # 和 Logger.removeHandler() 添加和删除一个Handler
Logger.addFilter() # 添加一个Filter,过滤作用
Logging.Handler # Handler基于日志级别对日志进行分发,如设置为WARNING级别的Handler只会处理WARNING及以上级别的日志。
# Handler常用函数有:
setLevel() 设置级别
setFormatter() 设置Formatter
import logging
#创建一个logger
logger = logging.getLogger()
#Log等级总开关
logger.setLevel(logging.INFO)
# 创建一个handler,用于写入日志文件,mode='w'表示程序重启重写日志
fh = logging.FileHandler('./logging.log',mode='w')
# 定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
# 输出到file的log等级的开关
fh.setLevel(logging.ERROR)
logger.addHandler(fh)
try:
a = 1 / 0
except BaseException as e:
print(e)
logging.exception(e)
finally:
print('is finally')
'''
logging.py文件:
2022-02-14 21:11:28,643 - main.py[line:20] - ERROR: division by zero
Traceback (most recent call last):
File "d:\python_project\demo\main.py", line 17, in <module>
a = 1 / 0
ZeroDivisionError: division by zero
'''
%(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 # 打印日志信息
错误也是一个class,所以需要继承一个合适的错误类
使用raise
可以抛出一个错误
class NameError(ValueError):
pass
def setName(name):
if(len(name) > 4):
raise NameError('名称不合法:%s' % name)
setName('一二三四五')
'''
Traceback (most recent call last):
File "d:\python_project\demo\main.py", line 11, in <module>
setName('一二三四五')
File "d:\python_project\demo\main.py", line 9, in setName
raise NameError('名称不合法:%s' % name)
__main__.NameError: 名称不合法:一二三四五
'''