Python MISC
异常处理
正常来说, 一个全部组件都完整的异常处理包含一个 try, 多个 except, 和一个 finally. 正常的执行顺序是:
(1) 如果 try 里没有任何异常. 那么执行顺序是: 先执行 try, 再执行 except, 再执行 finally
(2) 如果 try 里发生异常, 并且有对应的 except 捕获了这个异常, 并且这个 except 执行完也没有引发新的异常, 那么执行顺序是: 先执行 try, 再执行 except, 再执行 finally
有如下特殊情况:
(1) try 里发生异常, 且没有对应的 except 将其捕获, 执行顺序是: 先执行 try, 再执行 finaly (2) try 里发生异常, 有对应的 except, 但 except 里又引发了新的异常, 那么执行顺序是: 先执行 try, 然后执行 except, 然后执行 finally, 最后将新异常抛出 (3) try 里发生异常, 有对应的 except, except 里引发了新异常, 而 finally 里也引发了新异常, 那么执行顺序是: 先执行 try, 然后执行 except, 然后执行 finally, 最后将 finally 的异常抛出
def bar():
try:
try:
return 1 / 0
except ZeroDivisionError as e:
print("Enter except")
raise ValueError("1")
finally:
print("Enter finaly")
raise ValueError("123")
except Exception as e:
print(e.args[0])
bar()
# 输出:
# Enter except
# Enter finaly
# 123除此以外, break 和 return 也同样遵循 finally 覆盖 except 覆盖原始 try 的逻辑.
注意: finally 语句块种不能使用 yield 语法
另外, 有一个这种特殊模式, 这种模式是常见且实用的
def foo():
try:
yield db
finally:
db.close()
def main():
db = next(foo())
# 使用 db 去做别的事情
# ...
# 退出 main 时, 会自动执行 finally 语句块的内容
# 也可以像这样更明确地写, 但比较笨拙, 不优雅, 不常见
def main():
gen = foo()
db = next(gen)
# ...
gen.close()具体的理解如下: 在 main 函数中, next(foo()) 会让生成器执行到 yield db 为止, 而退出 main 函数时, foo() 这个生成器会被销毁, 而销毁生成器会触发生成器的 close 方法, 而 close 方法做的事情是在生成器当前为止引发一个 GeneratorExit, 这导致生成器执行 finally, 也就执行了 db.close(), 随后这个 GeneratorExit 被抛出, 能被生成器的 close 方法正确处理
使用生成器/上下文管理器处理数据库连接
注意: 两种方式的底层是一样的, 本质上是生成器的 close 起作用, 前者更底层些. 但在使用者来说, 前者需要使用 next(gen) 的语法, 后者用 with 语法, 后者会更友好些. 但从内部实现来说, 前者理解起来难度反而更小些(后者如果要细细研究还需要知道 __enter__, __exit__, contextlib.contextmanager)
使用生成器
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./dialogues.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def main():
db = next(get_db())
# TODO: labeling_item
db.add(labeling_item)
db.commit()
db.refresh(labeling_item)使用上下文管理器包装(更常见)
from contextlib import contextmanager
# 函数体和之前没有任何变化
@contextmanager
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def main():
with get_db() as db:
# TODO: labeling_item
db.add(labeling_item)
db.commit()
db.refresh(labeling_item)sys.path
# 假设当前目录的绝对路径是 <root>
python app/run.py # 那么sys.path包含的搜索路径是 <root>/app
python -m app.run # 那么sys.path包含搜索路径是 <root>
PYTHONPATH=<root>/app python -m app.run # 那么sys.path包含搜索路径是 <root> (最优先) 和 <root>/app (次优先), 因此可能会出现冲突假设目录结构如下:
myproj/
app/
main.py
utils/
helper.py其中 main.py 的内容如下:
from ..utils.helper import a
print(a)> cd myproj
> python -m app.main
ImportError: attempted relative import beyond top-level package
> cd ..
> python -m myproj.app.main
# okLast updated
Was this helpful?