SQLALchemy - Database Tables

SQLALchemy Core and SQLALchemy ORM SQLALchemy 分位两个模块:Core 和 ORM (Object-Relational Mapping)。 Core 模块包含对所有受支持数据库方言的集成逻辑,一组用于描述数据库表的类,用于 Python 语言生成 SQL 语句。 ORM 模块在 Python 应用程序中引入了一层抽象,使得许多数据库操作可以根据对 Python 对象执行的操作自动推导出来。 Database Engine SQLALchemy 使用 engine 对象来管理数据库连接,包含 Core 和 ORM 应用。 create_engine() 函数通过数据库 url 创建一个 engine。 格式为: {dialet}{+driver}://{username}:{password}@{hostname}:{port}/{database} 其中 SQLite 比较特殊,无需 driver。 对于导入 DATABASE_URL 有两种方式,一种是使用 load_dotenv() 加载 .env 文件的环境变量 import os from dotenv import load_dotenv from sqlalchemy import create_engine load_dotenv() engine = create_engine(os.environ['DATABASE_URL']) 另一种是通过 Pydantic BaseSettings import os from pathlib import Path from pydantic_settings import BaseSettings from pydantic impot SecretStr from functools import lru_cache from sqlalchemy import create_engine class Settings(BaseSettings): POSTGRESQL_HOST: str POSTGRESQL_PORT: int POSTGRESQL_USER: str POSTGRESQL_PASSWORD: SecretStr POSTGRESQL_DB: str @property def postgres_db_url(self) -> str: # 通常无需显示地写 driver,只有更换 dirver 时才需要写 return f'postgresql://{self.POSTGRESQL_USER}:{self.POSTGRESQL_PASSWORD.get_secret_value()}@{self.POSTGRESQL_HOST}:{self.POSTGRESQL_PORT}/{self.POSTGRESQL_DB}' class Config: env_file = str(Path(__file__).parent / '.env') case_sensitive = True extra = 'ignore' # 忽略额外字段 @lru_cache def get_settings() -> Settings: return Settings() # 使用示例 settings = get_settings() print('Host:', settings.POSTGRESQL_HOST) print('Password:', settings.PASSWORD.get_secret_value()) engine = create_engine(settings.postgres_db_url) create_engine() 函数有以下配置参数: ...

January 26, 2026 · 8 min · 1512 words · Starslayerx

Python Tricks Part 8: Modules and Packages

Python 程序由 modules 和 packages 组成,使用 import 语句导入。 Modules and the import Statement 任何 Python 源文件都可以作为一个模块导入,例如下面 module.py 代码: a = 37 def func(): print(f'func says that a is {a}') class SomeClass: def method(self): print('method says hi') print('loaded module') 改文件包含一些常见的编程元素,包括一个全局变量、一个函数、一个类定义 和 最后的语句。 通过下面方法导入: import module module.a module.func() s = module.SomeClass() s.method() 执行 import 会发送下面这几件事: 加载模块源码,如果找不到抛出 ImportError 创建新模块对象。该对象作为模块内所有全局定义 global defintions 的容器,被称为 “命名空间” namespace 该模块源码在新创建的模块命名空间内执行 如果没有错误发生,调用者会创建一个名称,指向新的模块对象。该名称与模块名称一致,但不包含任何文件后缀。 这些步骤中,第一步是最复杂的。新手容易犯的错误就是使用错误的名称或将代码放到了未知的位置。 且模块文件必须放在 sys.path 所包含的文件路径中,且文件名称要遵循和 python 变量一样的规则。 剩下的步骤都隔离在一个模块中,因此不用担心不同模块间命名冲突的问题。 Python import 会执行所有导入的源码,因此导入上面模块会输出 loaded module。 ...

January 20, 2026 · 7 min · 1370 words · Starslayerx

Python Tricks Part 7: Classes and Object-Oriented Programming

使用 vars() 方法返回对象的属性和值的字典对象,不带参数时,返回当前局部作用域的变量字典 vars() # 相当于 locals() class Person: name: str age: int def __init__(self, name, age): self.name = name self.age = age self.city = "Beijing" p = Person("Alice", 25) print(vars(p)) Attribute Access 一个示例只有三种基础的方法:getting, setting 和 deleting 属性 class Attribute: owner: str blance: float def __init__(self, owner: str, balance: float): self.owner = owner self.balance = balance def __repr__(self): return f"Account({self.owner!r}, {self.balance!r})" def deposite(self, amount: float): self.balance += amount def withdraw(self, amount: float): self.balance -= amount def inquiry(self) -> float: return self.balance 例如 a = Account("Guido", 1000.0) a.owner # get a.balance = 75 # set del a.balance # delete Python 中的一切都是一个动态过程,几乎没有什么限制。 例如,可以给已创建的对象添加新属性: a = Account("Guido", 1000.0) a.creation_date = "2019-02-14" a.nickname = "Fromer BDFL" 有时候不适用点 . 操作符来执行任务,而是通过将属性名传递给 getattr(), setattr() 和 delattr() 函数来实现。 hasattr() 函数允许你测试一个已存在的属性: a = Account("Guido", 1000.0) getattr(a, "owner") setattr(a, "balance", 750.0) delattr(a, "balance") hasattr(a, "balance") # False getattr(a, "withdraw")(100) # Method Call # a = Account("Guido", 650.0) getattr() 函数可以携带一个默认值,如果想要查看一个可能不存在的属性,可以这样实现: ...

January 13, 2026 · 20 min · 4105 words · Starslayerx

Python Tricks Part 6: Generators

生成器是 Python 中一种强大的特性,其通常被介绍为一种定义新型迭代模式的便捷方式。 但生成器从根本上改变了整个函数执行的模式,本篇文章重点关注:生成器、生成器委托、基于生成器的协程,以及生成器的其他内部机制。 Generators and yield 如果一个函数使用 yield 关键字,这定义了一个生成器。 生成器的主要用户是生成用于迭代的值。 例如: def countdown(n): print("Counting down from", n) while n > 0: yield n n -= 1 # Example use for x in countdown(n): print("T-minus", x) 如果调用该函数则不会开始执行: c = countdown(10) # <generator object countdown at 0x106faa260> 相反,会创建一个生成器对象。 该生成器对象只有在你迭代它的时候才会开始执行,使用的一种方式是调用 next()。 例如: next(c) # Counting down from 10 # 10 next(c) # 9 当调用 next() 时,生成器函数会执行语句直到遇到 yield 语句。 yield 语句会返回一个结果,此时函数的执行被挂起,直到再次调用 next()。 当其暂停的时候,函数会保留所有的本地变量和执行环境。 恢复执行时,程序会从 yield 之后的语句继续运行。 next() 是调用生成器上 __next__() 方法的简写形式。 例如,你可以这样: c.__next__() # 8 c.__next__() # 7 通常不会在生成器直接使用 next(),而是使用 for 或其他一些语句: ...

January 9, 2026 · 6 min · 1113 words · Starslayerx

Python Tricks Part 5: Functions

函数是 Python 的基础模块,本篇会介绍 function application 函数定义、function application 函数应用、scoping rules 作用域规则、closures 闭包、decorators 装饰器和其他函数式编程特性。 特别关注不同的编程习惯 idioms、求值模型以及与函数相关的模式。 Default Arguments 你可以通过在函数定义处赋值的方式,给函数的参数添加默认值,例如: def split(line, delimiter=","): statements 当一个函数定义了默认参数的时候,其右侧都必须是含有默认值的可选参数。 默认函数参数会在函数首次定义的时候调用一次,这有时会导致出乎意料的行为: def func(x, items=[]): items.append(x) return items func(1) # returns [1] func(2) # returns [1, 2] func(3) # returns [1, 2, 3] 注意到每次掉都将函数默认值给修改了,要避免这种行为,使用 None 并进行检查: def func(x, items=None): if not items: items = [] items.append(x) return items 通常来说,建议只使用不可变对象作为默认参数值。 Variadic Arguments 可变参数 如果在最后一个参数前使用 asterisk 星号作为前缀,函数就可以接受可变数量的参数。 def product(first, *args): result = first for x in args: result = result *x return result product(10, 20) # 200 product(2, 3, 4, 5) # 120 在这个例子中,所有的额外参数都作为一个元组放在 args 变量中。 对于元组,你可以使用序列的标准操作处理,如迭代、切片,解包等。 Keyword Arguments 函数参考可以通过显示命名每个参数并指定值来提供函数参数。 这被称为 keyword arguments 关键字参数,例如: ...

December 19, 2025 · 16 min · 3236 words · Starslayerx

Python Tricks Part 4: Objects, Types and Protocols

Essential Concepts 每个存储在程序中的数据都是一个对象,每个对象都有一个 identity, type and value (身份、类型和值)。 例如 a = 42 会创建一个整数为 42 的类型,该对象的 identity 是内存中的一个数字,代表其在内存中的位置,a 是这个类的标签,指向这个特定的内存位置,标签本身并非对象的一部分。 object 对象的类型,也被称为 class 类,该类定义了对象的内部数据表示,和支持的方法。 当特定类型的对象创建后,该对象被称为该类的 instance “实例”。 当实例创建后,其 identity 就不会改变。 如果一个对象的值可以被修改,则该对象是可变的 mutable。 如果一个对象的值不可以被修改,则该对象是不可变的 unmutable。 一个持有对其他对象引用的对象,被称为容器。 对象通过其属性来表征,属性是于对象关联的值,通过点 . 运算符来访问。 属性可以是一个简单的值,例如一个数字,也可以是一个被调用以执行某些操作的函数。 这类函数被称为方法,例如下面这个例子 a = 34 # Create an integer n = a.number # Get the numberator (an attribute) b = [1, 2, 3] # Create a list b.append(4) # Add a new element using the append method Object Identity and Type 内置函数 id() 返回对象的 identity,该 identity 是一个整数,通常对应 object 的内存地址。 is 操作符会对比两个对象的 identity,type() 返回对象的类型。 ...

December 18, 2025 · 9 min · 1715 words · Starslayerx

Shell

Terminal 终端是提供文本用户界面的程序,早期终端是集成设备,键盘和屏幕集成在一起,现在终端知识简单的应用程序。 除了基本的输入输出外,终端还支持将所谓转义序列或转移码,用于光标和屏幕处理,并可能支持颜色。 例如,使用 Ctrl-h 会删除前面一个字符。 环境变量 TERM 可能使用了终端模拟器,其配置可以通过 infocmp 获得 # Reconstructed via infocmp from file: /usr/share/terminfo/74/tmux-256color tmux-256color|tmux with 256 colors, am, hs, km, mir, msgr, xenl, colors#256, cols#80, it#8, lines#24, pairs#32767, acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, clear=\E[H\E[J, cnorm=\E[34h\E[?25h, cr=^M, ... 显示内容看上去很混乱,这些是 Shell shell 是一个运行在终端内部的程序,充当命令解释器。 shell 通过流提供输入和输出处理,支持变量,有一些内置命令,处理命令执行和状态,支持交互式和脚本使用。 最初的 shell 叫做 Bourne shell sh,即作者名字命名,现在通常被 bash 替代,即 “Bourne Again Shell” 的缩写。 file -h /bin/sh Stream shell 为每个进程提供了三个默认的文件描述符 (FD),用于输入和输出: ...

December 18, 2025 · 5 min · 995 words · Starslayerx

Python Tricks Part 3: Program Structure and Control Flow

Exceptions 异常 exceptions 具有一些标准属性,这些属性在需要针对错误执行进一步操作的代码中可能非常有用。 e.args 这是引发异常时提供的元组,在大多数情况下,这是一个包含描述错误字符串的单元元素元组。 对于 OSError 异常,其值是一个包含整数错误码、字符串错误消息,以及可选文件名的 2 元组或 3 元组。 e.__cause__ 如果该异常是在处理另一个异常时有意引起的 raise ... from ...,Python 会将这两个异常链接起来,形成异常链。 e.__context__ 如果异常是处理异常时无意间导致的,则会产生 e.__context__。 e.__traceback__ 与异常相关联的堆栈回溯对象。 用于存储异常值的变量仅在相关的 except 块内部可以访问,一但控制论离开该块,该变量将变为未定义。 try: int('N/A') except ValueError as e: print('Failed:', e) print(e) # Fails -> NameError. 'e' not defined 多异常处理块通过多个异常子句指定: try: # do something except TypeError as e: # Handle Type error except ValueError as e: # Handle Value error 当然也可以在单个子句中处理多个异常类型 try: # do something except (TypeError, ValueError) as e: # Handle Type or Value error 可以使用 pass 忽略报错 ...

December 17, 2025 · 7 min · 1335 words · Starslayerx

Python Tricks Part 2: Operators, Expressions and Data Manipulation

Literals 整数 04 0b101010 # Binary 二进制 0o52 # Octal 八进制 0x2a # Hexadecimal 十六进制 浮点数,内部使用 IEEE 754 双精度存储 4.2 42. 4.2e+2 # 科学记数法 4.2E2 -4.2e-2 数字类型字面量还可以使用 _ 来方便阅读 123_456_789 Truth Values true: 非0数字、任何非空的字符串,列表,元组或字典 false: 0、None、空列表,元组或字典 Operations Involving Iterables 任何可迭代对象都可以展开,如 list, tuple and set,都通过星号(*)。 items = [1, 2, 3] a = [10, *items, 1] # [10, 1, 2, 3, 1] b = (*items, 10, *items) # [1, 2, 3, 10, 1, 2, 3] c = {10, 11, *items} # {10, 11, 1, 2, 3} 在上面例子中,item 简单的被粘贴到 list, tuple, set 中,就和手动输入进去一样。 有时候这种展开 expansion 被称为“展开操作符” splatting。 ...

December 16, 2025 · 2 min · 328 words · Starslayerx

Python Tricks Part 1: Basis

这篇文章总结一些平时容易被忽略的 Python 知识 Primitives, Variables and Expressions print(f"{year:>3d} {principal:0.2f}") >3d 指至少 3 位十进制数,右对齐 0.2f 指精度为 2 位的浮点数 Arithmetic Operators round(x, [n]): 该函数采用 Banker’s Rounding 银行家舍入法,也叫 四舍六入五成双,当要舍弃的数字正好是 5 时 前一位是偶数 → 向下舍去(向偶数靠拢) 如果前一位是奇数 → 向上进位(向偶数靠拢) 这样做的目的是减少舍入误差的累积,在统计学和金融计算中更为公平。 # 常规四舍五入(Python实际行为是银行家舍入) print(round(1.5)) # 2 (1是奇数,5进位) print(round(2.5)) # 2 (2是偶数,5舍去) print(round(3.5)) # 4 (3是奇数,5进位) print(round(4.5)) # 4 (4是偶数,5舍去) # 更复杂的例子 print(round(1.25, 1)) # 1.2 (2是偶数,5舍去) print(round(1.35, 1)) # 1.4 (3是奇数,5进位) print(round(1.251, 1)) # 1.3 (因为后面还有1,不是正好5,正常进位) 银行家舍入法是 IEEE 754 标准推荐的方式,Python、R、NumPy 等都采用这种舍入方式,能有效减少大量数据计算时的统计偏差。 Python 二进制运算符会将整数视为 2’s complement binary representation 二进制补码,并且符号位会在左侧无限扩展。 此外,Python 不会截断二进制,也不会溢出。 ...

December 15, 2025 · 4 min · 662 words · Starslayerx