Python 装饰器,别再只会加个 @log 了(彻底搞懂原理 + 应用)
装饰器可以说是 Python 最让人“又爱又恨”的语法糖之一。
刚开始我们觉得它只是个语法糖,可一旦深入,就发现它其实可以做很多:权限验证、日志记录、缓存、参数校验、接口拦截、自动重试……几乎所有能想到的“函数增强”都可以用装饰器实现。
但问题也随之而来:
装饰器到底是怎么“包裹函数”的?
为什么装饰器函数要写三层?
@wraps 到底有什么用?
装饰器能不能传参数?
多个装饰器嵌套,顺序怎么搞清楚?
这篇文章,让我们不依赖死记硬背,把装饰器的原理和实际用法讲清楚讲透。
装饰器是啥?
装饰器本质上是一个“接收函数,返回新函数”的函数。
说白了就是:它接收一个函数,然后生成一个“升级版”的函数,把你想加的功能(比如打印日志)包在原函数前后。
最小例子:
def outer(func): def inner(): print("before") func() print("after") return inner@outerdef say_hello(): print("H ...
高效提取数据库分析上下文:如何使用 Parquet 文件为大模型提供数据理解能力
在利用大语言模型(LLM)自动生成分析代码时,我们经常面临一个问题:无法将完整的数据库数据作为提示词提供给模型,因为数据量太大、隐私敏感或成本高。
本文将介绍一种高效、结构化的数据摘要方法,帮助将数据库数据导出并转换为结构描述 + 示例数据,以便 LLM 快速理解上下文,从而生成正确的数据处理代码。
场景背景开发过程中可能正处于如下场景中:
从数据库中查询了大量数据(百万行以上)
想让大模型帮你写分析代码
但无法把全部数据放进提示词(Token 受限)
我们只想告诉模型:“这个文件包含哪些字段?每列什么类型?长什么样?”
于是,我们需要一种方法来:
✨ 快速保存查询数据,并提取 schema 和部分示例,生成简洁的上下文描述供 LLM 使用。
为什么不用 CSV?虽然 CSV 是最常见的数据导出格式,但它存在很多局限:
问题
说明
无数据类型信息
所有内容都是字符串,模型不知道哪些是数字、时间等
文件大
无压缩,占空间大,加载慢
不支持嵌套结构
如果有 JSON 列、数组列,几乎无法兼容
不利于筛选
无列式存储,不方便快速读取前几行
解决方案: ...
Python 对象复制的坑:浅拷贝 vs 深拷贝
在 Python 开发中,我们常常希望复制一个对象,以避免直接修改原始数据。然而,“复制”这个操作远没有看起来那么简单。Python 提供了两种常见的复制方式:浅拷贝(shallow copy)和 深拷贝(deep copy),它们在处理嵌套对象时行为大不相同。
这篇博客会一步步解释它们之间的区别、使用场景,以及如何避免常见的坑。
赋值 ≠ 复制先看一个简单例子:
a = [1, 2, 3]b = ab[0] = 100print(a) # [100, 2, 3]
这里的 b = a 并没有复制 a,而是使 b 指向 a 的同一个内存地址,两者是同一个对象。
浅拷贝 (shallow copy)import copya = [1, [2, 3]]b = copy.copy(a)b[0] = 100b[1][0] = 200print("a:", a) # [1, [200, 3]]print("b:", b) # [100, [200, 3]]
浅拷贝特点
创建了一个新对象,但只复制了第一层结构
内部嵌套对象仍然引用原始对象
...
LangChain中的Runnable接口
Runnable 接口是使用 LangChain 组件的基础,它在很多组件中实现,例如语言模型(language models)、输出解析器(output parsers)、检索器(retrievers)、编译的 LangGraph 图,该接口允许开发人员以一致且可预测的方式与各种 LangChain 组件进行交互。
Runnable 接口概述Runnable 的方式定义了一个标准的接口,允许 Runnable 组件:
Invoked:将单个输入转换为输出。
Batched:多个输入被有效地转换为输出。
Streamed:输出在生成时进行流式传输。
Inspected:可以访问有关 Runnable 的输入、输出和配置的示意图信息。
Composed:可以组合多个 Runnable,使用 LangChain 表达语言(LCEL)协同工作,以创建复杂的管道。
优化的并行执行(batch)LangChain Runnables 提供内置batch(和batch_as_completed)API,允许您并行处理多个输入。
当需要处理多个独立输入时,使用这些方法可以显著提高性能,因为处理可 ...
如何让 LLM 的返回值流式输出?
如何让 LLM 的返回值流式输出?流式输出大模型收到输入后并不是一次性生成最终结果,而是逐步地生成中间结果,最终结果由中间结果拼接而成。用流式输出的方式调用大模型 API,能够实时返回中间结果,减少用户的阅读等待时间,并降低请求的超时风险。
简介流式输出,也称为流式传输,指的是服务器持续地将数据推送到客户端,而不是一次性发送完毕。这种模式下,连接一旦建立,服务器就能实时地发送更新给客户端。
相比非流式输出,流式输出可以实时地将中间结果返回,您可以在模型进行输出的同时进行阅读,减少等待模型回复的时间;并且当输出内容较长时,有效降低请求超时的风险。
请求超时错误的报错信息:Request timed out, please try again later. 或 Response timeout。
适用场景流式输出的典型应用场景包括实时消息推送、股票行情更新、实时通知等,任何需要服务器向客户端实时传输数据的场合都可以使用。
与普通请求的区别与传统的 HTTP 请求不同,普通请求是基于请求-响应模型,客户端发送请求后,服务器处理完毕即刻响应并关闭连接。流式输出则保持连接开放,允许服务器连续 ...
协程 & 异步编程(asyncio)
协程 & 异步编程(asyncio)协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。例如:
def func1(): print(1) ... print(2) def func2(): print(3) ... print(4)func1()func2()
上述代码是普通的函数定义和执行,按流程分别执行两个函数中的代码,并先后会输出:1、2、3、4。但如果介入协程技术那么就可以实现函数见代码切换执行,最终输入:1、3、2、4 。
1. 协程的实现在Python中有多种方式可以实现协程,例如:
greenlet,是一个第三方模块,用于实现协程代码(Gevent协程就是基于greenlet实现)
yield,生成器,借助生成器的特点也可以实现协程代码。
asyncio,在Python3.4中引入的模块用于编写协程代码。
async & awiat,在Python3.5中引入的两个关键字,结合asyncio模块可以更方便的编写协程代码。
1.1 greenletgreen ...
舜桀的算法笔记
这里主要记录我学习算法的历程~
主要参考资料为labuladong的算法笔记
下面开始学习算法吧!
数据结构基础Java基础Java标准库数据结构的基本用法1.数组int m = 5, n = 10;// 初始化一个大小为 10 的 int 数组// 其中的值默认初始化为 0int[] nums = new int[n]; // 初始化一个 m * n 的二维布尔数组// 其中的元素默认初始化为 falseboolean[][] visited = new boolean[m][n];
2.字符串StringJava 字符串不支持用 [] 直接访问其中的字符,且不能直接修改,需要转化为 char[] 类型才能修改。
String s1 = "hello world";// 获取 s1[2] 的那个字符char c = s1.charAt(2);char[] chars = s1.toCharArray();chars[1] = 'a';String s2 = new String(chars);// 输出:hallo worldSyste ...
动手学习深度学习-记录
torch.arangetorch.arange(start, end, step=1, out=None) -> Tensor#返回一个1维张量,长度floor((end - start)/step)#包含从start到end,以step为步长的一组序列值(默认步长为1)#参数##start(float) - 序列的起始点##end(float) - 序列的终止点##step(float) - 相邻点的间隔大小##out(Tensor, optional) - 结果张量#例子x = torch.arange(12)y = torch.arange(2, 9)z = torch.arange(2, 9, 2)print(x)print(y)print(z)#结果tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])tensor([2, 3, 4, 5, 6, 7, 8])tensor([2, 4, 6, 8])
torch.zerostorch.zeros(*sizes, out=None) -> Tensor#返 ...
数据结构算法总结-自用
数据结构算法整理By 舜桀BB循环双链表的存储结构typedef struct DNode{ ElemType data; struct DNode *prior,*next;}DNode,*DLinklist;//初始化空的循环双链表bool InitDLinkList(DLinklist &L){ L = (DNode *) malloc(sizeof(DNode)); //分配一个头结点 if(L==NULL) //内存不足,分配失败 return false; L->prior = L; //头结点的prior指向头结点 L->next = L; //头结点的next指向头结点 return false;}//判断循环双链表是否为空bool Empty(DLinklist L){ if(L->next==L) return true; else return false; ...