06.30.每日总结

6月30日总结

## 上午:农民工项目 新增数据功能完成 写一下和千问的问答过程记录些有价值的知识问:数据库execute的参数提交

传递给execute的数据必须是一个元组或者列表。如果是一个字典,需要使用命名占位符

问:什么是异步函数

在编程中,数据库查询(I/O操作)是非常耗时的。如果写 async def insert(data),就等于告诉 Python:“这个函数里会有耗时的等待(比如等数据库返回),所以不要在这里死等,你可以先去处理别的任务

问:针对为什么有时候警告同步异步不匹配,但是路径装饰器里没有这种警告

路由函数:是框架调用的,框架知道它是异步的,所以框架会帮你 await,不会报警告。
路由内部调用的函数:是你自己调用的,如果你调用的是异步函数,你必须亲自负责 await,否则 Python 就会警告你“拿了号码牌不去取餐”。

使用aiomysql步骤(具体见模拟项目代码)

1 安装异步驱动
2 修改数据库连接方式
3 修改SQL执行函数
4 在路由中调用
补充:在fastapi启动和关闭时要及时处理数据库连接池,否则它不会正常初始化和释放(方法见模拟项目)

cant set attribute问题

它不是数据库连接或SQL的问题,而是因为你原来的同步代码中,某些地方直接对 cursor 或 connection 对象进行了属性赋值(例如 cursor.xxx = yyy),而 aiomysql 的游标/连接对象是只读的,不允许动态设置属性。result = cursors.DictCursor.fetchone(self=db)
原因分析:
1 cursors.DictCursor 是一个类(Class),不是实例对象。
2 当你写 fetchone(self=db) 时,Python 试图将 db(一个 aiomysql 的 Cursor 实例)强制赋值给 DictCursor.fetchone 方法的 self 参数。
3 aiomysql 的底层 Cython/C 扩展严格限制了属性修改,这种跨类型的强制 self 绑定直接触发了底层的 can't set attribute 保护机制。

枚举类改为纯整数枚举

from enum import IntEnum

class CraftEnum(IntEnum):

钳工 = 1

铆工 = 2

钻工 = 3

车工 = 4

数控人员 = 5


问:表单传参,一次性传多个值,表单有name 但是具体的各种文本控件没有,可以吗?

不行,给 form 起 name 和给表单控件起 name 是完全不同的两件事。
区别在于:
form name="xxx":这是给表单容器起名。它的作用是让 JavaScript 能通过 document.forms['xxx'] 找到这个表单元素本身。它不会把任何数据发送给后端。
input name="xxx":这是给数据字段起名。它的作用是告诉浏览器:“当提交表单时,把这个输入框里的值以 xxx 为键名打包发送”。这才是后端接收数据的唯一依据。

问:数据校验类转化为字典的方法
.model_dump()

一些重要用法:
1 枚举值自动转换 model_dump() 默认会将 GenderEnum.male 转为它的实际值 '1',不需要你手动 .value
2 Pydantic v1 用户注意 如果你用的是老版本 Pydantic v1,方法是 .dict() 而不是 .model_dump()
3 按需导出字段 如果数据库表没有某个字段,可以排除:data.model_dump(exclude={'worker_bank_number'})
4 字段名映射 如果 Python 字段名和数据库列名不一致,可以在model_dump(by_alias=True) 配合 Field(alias="db_column_name")使用

问:给SQL语句传参直接传入字典+字典解包可以吗

不可以,应该使用命名占位符+字典解包
将 %s 改为 %(字段名)s,数据库驱动就会自动从字典中按 key 取值并安全地绑定参数:

async def insert(data: dict) -> bool:
insert_clause = ''' INSERT INTO biz_worker ( worker_name, worker_age, worker_gender, worker_craft, worker_id_number, worker_bank_number) VALUES (%(worker_name)s, %(worker_age)s, %(worker_gender)s,%(worker_craft)s, %(worker_id_number)s, %(worker_bank_number)s '''
问:关于通过数据校验时和最后传入数据库时数据的类型该怎么处理的问题

在 Python 后端开发中,有一个黄金原则:“以业务本质定义模型,以数据库需求做最后转换”。
对于身份证号和银行卡号:
1 永远在 Pydantic 模型中定义为 str。因为它们在业务上属于“标识符”,而不是用来做加减乘除的“数值”。
2 在数据库层面:如果数据库设计者把它们设计成了 INT 或 BIGINT,你在传给数据库之前用 int() 转换一下即可。
总结:你只需要在 Pydantic 中安心地用 str 做长度校验,然后在调用 await insert(worker_dict) 之前,把需要存为整数的字段 int() 一下,就能完美兼顾“数据安全性”和“数据库兼容性”了!

## 下午:上课:正则表达式

正则表达式的结构

[边界/分组开始] + [种类(字符)] + [数量(量词)] + [边界/分组结束]

在实际编写正则时,标准的书写顺序通常是这样的:

[边界/分组开始] + [种类(字符)] + [数量(量词)] + [边界/分组结束]
写正则时,心里默念这个顺序:我要匹配什么?(写种类:\d, \w, [a-z])
我要匹配几个?(写数量:*, +, {3})
我要限定位置吗?(写边界:^, $, \b)
种类在前,数量在后,这是正则表达式的铁律。

正则语法:

正则表达式的核心格式通常由以下几部分组成:

① 字符匹配

. :匹配除换行符外的任意单个字符。
\d :匹配数字(等价于 [0-9])。
\w :匹配字母、数字、下划线(等价于 [a-zA-Z0-9_])。
\s :匹配空白字符(空格、制表符、换行符等)。

② 数量词(量词)

* :匹配 0 次或多次。
+ :匹配 1 次或多次。
? :匹配 0 次或 1 次。
{n} :精确匹配 n 次。
{m,n} :匹配 m 到 n 次。

③ 边界与定位

^ :匹配字符串的开头。
$ :匹配字符串的结尾。
\b :匹配单词边界。

④ 分组与捕获

(...) :将括号内的内容作为一个整体(分组),并捕获匹配到的内容。
(?:...) :非捕获分组,只作为整体匹配,不保存结果。
| :或(OR)操作符。

返回值与 Match 对象:match()和search()才返回match对象

匹配成功:一旦找到匹配项,立即返回一个 Match 对象,封装了完整的匹配上下文信息。
匹配失败:若全程未匹配,返回 None。
如果匹配成功,你可以通过 Match 对象的以下核心方法提取数据:
group():默认返回整个匹配的文本内容。
groups():返回所有捕获组(括号内的内容)组成的元组。
start() / end():返回匹配内容在原字符串中的起始与结束索引。
span():以二元组形式返回 (start, end) 索引位置。

贪婪模式和非贪婪模式

1. 贪婪模式(Greedy):默认行为,尽可能多地匹配
在 Python 中,所有的数量词(*、+、?、{m,n})默认都是贪婪的。
性格:“贪得无厌”。只要符合规则,它会一直往后匹配,直到遇到下一个不符合规则的条件为止。
2. 非贪婪模式(Non-greedy / Lazy):加上 ?,尽可能少地匹配
只要在数量词后面加上一个 ?(例如 *?、+?、??),它就会变成非贪婪模式。
性格:“见好就收”。只要匹配到了最少的数量,并且满足了后面的条件,它就立刻停止。

正则表达式的分组

在正则表达式中,分组主要是通过 小括号 () 来实现的。它主要有三大核心作用:
作用一:精确提取(只要括号里的内容)
默认情况下,re.match()或re.search()返回的是整个匹配到的字符串。但如果你只想要字符串中的某一部分,就可以用()把它圈起来。
作用二:将多个字符作为一个整体(配合数量词)
数量词(如 *, +, {m,n})默认只作用于它紧挨着的前一个字符。如果你想让数量词作用于一长串字符,就必须用()把它们打包。
作用三:反向引用(自己引用自己)
这就是我们上一轮讲过的 \1、\2。括号不仅能把内容“抓”出来,还会给它们编号。
第1个左括号(捕获的内容,在正则内部可以用\1再次调用。
第2个左括号(捕获的内容,可以用\2调用。

下午:剩余时间:写作业 C语言布尔类型 数据的类型转换

其实C语言没有布尔类型,本质也是整型(0和1)
相关头文件&<;stdbool.h>
变量计算
隐式转换和显示转换
收窄转换(建议不要这么做)和拓宽转换(无风险)

晚上:

写总结