📘
notes
  • Center
  • Language
    • Python
      • Python environment
      • Advanced Python
      • Python Standard Libary
      • Python Third Party
      • CPython
      • Python MISC
    • C++
      • C++ Draft
    • Java
      • JavaSE
    • SQL
      • SQL
    • Rust
      • Rust
  • Tool
    • Git
    • Docker
    • Database
    • Linux & Shell
      • shell-cheet-sheet
    • Vim
    • gcc & make & cmake
    • misc
  • Note
    • DL
      • DL-deploy
      • huggingface
      • pytorch-lightning
      • Pytorch
      • TensorFlow
    • NLP
    • CV
      • OpenCV
      • mmlab
    • Audio
    • GNN
    • CUDA
    • Triton
    • TODO LIST
    • Draft
  • Learn
    • C++ Primer
    • C++ Primer Plus
    • CSAPP
    • CPython Internals
Powered by GitBook
On this page
  • 异常处理
  • 使用生成器/上下文管理器处理数据库连接
  • sys.path

Was this helpful?

Edit on GitHub
  1. Language
  2. Python

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
# ok
PreviousCPythonNextC++

Last updated 1 month ago

Was this helpful?