Python 踩坑记
前言
回归 Python 栈,相较 Go 的 Coding,Python 确实偏向复杂,看似编码方便快捷的背后,是越来越庞杂的细枝末节,稍不注意就是偏差。如果项目只是“能跑就行”,那大概率遍地是坑。开启踩坑记~
内存泄漏
服务内存缓慢持续上涨,内存泄漏,Python 相对还是好找一些,第一反应全局 Mutable 的变量再被持续 append? 寻迹之下发现不是,而是另外一种形式,危险的 Mutable 默认参数!
def some_decorator(default={}):@functools.wraps(func)def wrapper(*args, **kwargs):res = func(*args, **kwargs)if isinstance(kwargs['target'], dict):for k, v in kwargs['target'].items():default[k] = vrecord(default)return resreturn wrapper
本意是该装饰器在使用时会传入字典变量 default 用以后续记录内容中做一些基本信息植入。当不传时默认给一个空 dict。
问题就出现在这。
参数默认值的设定,都是在定义时设置一次,也就是说 default={} 的默认参数,并不是在每次调用时候,去执行 new 一个新 dict 传入 default,那么在大量都是缺省default 参数指定的默认值调用中,后续装饰器的内就会去不停写入 default 默认值的 dict 中的内容,如果外层 kwargs['target'] 中的 kv 又是大面积不重复的 kv 对,因此这里就变成一个 内存泄漏点,并且实际行为也是不符合预期的。
优化上文,剔除可变参数:
def some_decorator(default=None):@functools.wraps(func)def wrapper(*args, **kwargs):local = default or {}res = func(*args, **kwargs)if isinstance(kwargs['target'], dict):for k, v in kwargs['target'].items():local[k] = vrecord(local)return resreturn wrapper
真的修复了么?当使用是,不传入行参给 default 本地 new 一个 dict 貌似可以了。
# 如果这样使用呢?
@some_decorator(default={'test_key': 'test_value'})
def some_fun(*args, **kwargs):pass
闭包陷阱,装饰器声明时候调用一次,此时 loacl 被固定指向了 声明时传入的对象,后续所有的修改,都是在持续修改该对象,还是潜在泄漏,修复如下:
def some_decorator():@functools.wraps(func)def wrapper(*args, **kwargs):local = kwargs.get('default', {})res = func(*args, **kwargs)if isinstance(kwargs['target'], dict):for k, v in kwargs['target'].items():local[k] = vrecord(local)return resreturn wrapper
pydantic
package 的安装使用不再赘述,参考 https://docs.pydantic.dev/latest/ 介绍,但是个别细节还是容易踩坑
首先是 从 BaseModel 集成来的子类定义式中的 变量命名
pydantic.BaseModel 的很大一个应用场景是 帮我们做一个 实例对象 属性类型的校验,下例会有什么效果?
import datetime
from pydantic import BaseModelclass Test(BaseModel):attr: dict_flag: strt = Test(attr={'time': datetime.datetime(2018, 1, 30, 13, 55, 28)}, _flag='hello'
)
print(t._flag)
print(t.json())
执行发
Traceback (most recent call last):File "/Users/machao/miniconda3/envs/py3.9/lib/python3.9/site-packages/pydantic/main.py", line 746, in __getattr__return self.__pydantic_private__[item] # type: ignore
KeyError: '_region'
_region 这种 ‘_’ 命名的私有变量不会再 实例对象中初始化,不妨 打印 t.__dict__ 看下便知道
注释 print(t._flag) 程序顺利执行,这里 attr 中的 datetime 对象 顺利序列化了……
普通的 json.dumps() 都是要手动转一下 datetime 的,这里 json 顺利就完成了。
这种宽字典,其中 可选存在 datetime 字段时,如果想做中间缓存,手动 json.dumps() 存入 kv 又会遭中。
pydantic = 2.6.4 的较新版本中,对于转化就仅有的几句介绍:model_dump_json,能够对常规josn.dumps 中不兼容的 datetime、date 或 UUID 做到兼容。
这里的内部实现没有明确说明,源码也没有,不过对于老一些的 pydantic = 2.6.4 他的兼容实现,就是类似调用 josn.dumps的时候指定 default,笔者在 旧版本上做过 debug,内部就是指定了不同类型的 encoder,可以参考 deprecated 中的 ENCODERS_BY_TYPE
线程
可能是 Go 的 Goroutine 用多了,Python threading 很多细节被颠覆了,总觉得开了 threading 不 join 主程序执行完退出整个程序就退了,分别看下原始 threading 包和 concurrent 中的 ThreadPoolExecutor
threading
def rand_sleep(i):time.sleep(random.randint(2, 10))print(f'close thread: {i}')# 样例一
if __name__ == '__main__':threads = [Thread(target=rand_sleep, args=(i,)) for i in range(5)]for t in threads:t.start()print('end main')
执行结果:
end main
close thread: 4
close thread: 3
close thread: 0
close thread: 1
close thread: 2Process finished with exit code 0
主程序执行完后会等待开的子线程执行完毕方才退出,顺序上,threads 开启后主程序继续往后执行,如果想要 threads 开启后阻塞住当前主进程,需调整如下
# 样例二
if __name__ == '__main__':threads = [Thread(target=rand_sleep, args=(i,)) for i in range(5)]for t in threads:t.start()# t.join() 此处 join 变成串行了for t in threads:t.join()print('end main')
执行结果
close thread: 1
close thread: 2
close thread: 3
close thread: 4
close thread: 0
end mainProcess finished with exit code 0
Thread 初始化中有个默认参数 daemon 用来设置 守护线程,在默认情况下是,当前进程,如果
在样例一的编码中,维持其他不变,添加参数 daemon=True
# 样例三
if __name__ == '__main__':threads = [Thread(target=rand_sleep, args=(i,), daemon=True) for i in range(5)]for t in threads:t.start()print('end main')
此时主进程执行完即退出,不会等待子线程,除非类似 样例二中 手动 join
concurrent.futures
Python 3.2 引入的并发库,让我们的开启并行的形式更简明,
# 样例一
if __name__ == '__main__':tasks = []with ThreadPoolExecutor(max_workers=10) as executor:for i in range(5):tasks.append(executor.submit(rand_sleep, i))print('end main')
执行结果
close thread: 4
close thread: 1
close thread: 0
close thread: 2
close thread: 3
end mainProcess finished with exit code 0
没有手动去调用 join 啊,为什么会阻塞主线程了,ThreadPoolExecutor 的上下文协议管理做了什么?
# ThreadPoolExecutor 的父类
class Executor(object):....def __exit__(self, exc_type, exc_val, exc_tb):self.shutdown(wait=True)return Falseclass ThreadPoolExecutor(_base.Executor):......def shutdown(self, wait=True, *, cancel_futures=False):with self._shutdown_lock:self._shutdown = Trueif cancel_futures:# Drain all work items from the queue, and then cancel their# associated futures.while True:try:work_item = self._work_queue.get_nowait()except queue.Empty:breakif work_item is not None:work_item.future.cancel()# Send a wake-up to prevent threads calling# _work_queue.get(block=True) from permanently blocking.self._work_queue.put(None)if wait:for t in self._threads:t.join()
shutdown 的方法里,默认是开启 wait 会 join 住主线程
# 样例二
if __name__ == '__main__':tasks = []executor = ThreadPoolExecutor(max_workers=10)for i in range(5):tasks.append(executor.submit(rand_sleep, i))print('end main')
执行结果
end main
close thread: 1
close thread: 0
close thread: 2
close thread: 3
close thread: 4Process finished with exit code 0
类似 threading 没有 join 的效果,翻看源码会发现,submit 内部会开启 Thread 效果仿佛类似 threading 等待底层的线程调度器来做逻辑处理,其实还是不一样
在 concurrent.futures.thread 中,定义了
def _python_exit():global _shutdownwith _global_shutdown_lock:_shutdown = Trueitems = list(_threads_queues.items())for t, q in items:q.put(None)for t, q in items:t.join()# 注册到 threading 包的 _register_atexit 中
threading._register_atexit(_python_exit)# threading 中如下
def _register_atexit(func, *arg, **kwargs):"""CPython internal: register *func* to be called before joining threads.The registered *func* is called with its arguments just before allnon-daemon threads are joined in `_shutdown()`. It provides a similarpurpose to `atexit.register()`, but its functions are called prior tothreading shutdown instead of interpreter shutdown.For similarity to atexit, the registered functions are called in reverse."""if _SHUTTING_DOWN:raise RuntimeError("can't register atexit after shutdown")call = functools.partial(func, *arg, **kwargs)_threading_atexits.append(call)
体会下这里的区别吧,并且在 concurrent.futures 还提供了两个模块级的函数 concurrent.futures.wait 和 concurrent.futures.as_completed 可以在细读下 doc
先到这~ 未完待续~
相关文章:
Python 踩坑记
前言 回归 Python 栈,相较 Go 的 Coding,Python 确实偏向复杂,看似编码方便快捷的背后,是越来越庞杂的细枝末节,稍不注意就是偏差。如果项目只是“能跑就行”,那大概率遍地是坑。开启踩坑记~ …...
搭建Spark单机版环境
在搭建Spark单机版环境的实战中,首先确保已经安装并配置好了JDK。然后,从群共享下载Spark安装包,并将其上传至目标主机的/opt目录。接着,解压Spark安装包至/usr/local目录,并配置Spark的环境变量,以确保系统…...
使用Flutter混淆技术保护应用隐私与数据安全
在移动应用开发中,保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具,帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆,并提供了相关的操作步骤和注意事项。 📝 摘要 本…...
ClickHouse初体验
1.clickHouse是啥? ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库(DBMS),使用 C语言编写,主要用于在线分析处理查询(OLAP),能够使用SQL查询实时生成分析数据报告 2.clickHouse的特点 2.1列式存储 对于列的聚合&…...
在k8s中部署高可用程序实践和资源治理
在k8s中部署高可用程序实践 1. 多副本部署1.1. 副本数量1.2. 更新策略1.3. 跨节点的统一副本分布1.4. 优先级1.5. 停止容器中的进程1.6. 预留资源 2. 探针2.1. 活性探针(liveness probes)2.2. 就绪探针(Readiness probe)2.3. 启动…...
WebView的使用与后退键处理-嵌入小程序或者 H5 页面
在使用 WebView 嵌入小程序或者 H5 页面时,通常会涉及到处理后退键的操作。在 Android 平台上,可以通过 WebView 的相关方法来实现后退键的处理。你可以按照以下步骤来实现: 在 Activity 或 Fragment 中找到 WebView 控件,并为其…...
【攻防世界】file_include (PHP伪协议+过滤器)
打开题目环境: 进行PHP代码审计,发现这是一个文件包含漏洞。 我们尝试利用PHP伪协议中的 php://filter来读取 check.php 中的内容。 构造payload 并提交: 发现payload被过滤掉了,我们就需要尝试使用不同的转换器。 PHP各类转换…...
Linux 内核中PHY子系统(网络):PHY驱动
一. 简介 PHY 子系统就是用于 PHY 设备相关内容的,分为 PHY 设备和 PHY 驱动,和 platform 总线一样,PHY 子系统也是一个设备、总线和驱动模型。 前面一篇文章学习了 PHY子系统中的 PHY设备。文章如下: Linux 内核中PHY子系统(网…...
【六 (1)机器学习-机器学习算法简介】
目录 文章导航一、机器学习二、基于学习方式的分类三、监督学习常见类型四、无监督学习常见类型五、强化学习常见分类 文章导航 【一 简明数据分析进阶路径介绍(文章导航)】 一、机器学习 机器学习是一门多领域交叉学科,涉及概率论、统计学…...
TCP服务端主动向客户端发送数据
C TCP 服务端和客户端通信的例子 在此基础上,要修改服务端代码,使其能够每秒向客户端发送当前时间,你需要添加一个循环,每次循环发送当前时间给客户端。同时,你需要在客户端代码中添加接收服务端发送的数据的逻辑。 …...
ObjectiveC-03-XCode的使用和基础数据类型
本节做为Objective-C的入门课程,笔者会从零基础开始介绍这种程序设计语言的各个方面。 术语 ObjeC:Objective-C的简称,因为完整的名称过长,后续会经缩写来代替;项目/工程:也称工程,指的是一个A…...
YOLOv9改进策略 :主干优化 | 无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023 RIFormer
💡💡💡本文改进内容: token mixer被验证能够大幅度提升性能,但典型的token mixer为自注意力机制,推理耗时长,计算代价大,而RIFormers是无需TokenMixer也能达成SOTA性能的极简ViT架构 ,在保证性能的同时足够轻量化。 💡💡💡RIFormerBlock引入到YOLOv9,多个数…...
원클릭으로 주류 전자상거래 플랫폼 상품 상세 데이터 수집 및 접속 시연 예제 (한국어판)
클릭 한 번으로 전자상거래 플랫폼 데이터를 캡처하는 것은 일반적으로 웹 페이지에서 정보를 자동으로 추출 할 수있는 네트워크 파충류 기술과 관련됩니다.그러나 모든 형태의 데이터 수집은 해당 웹 사이트의 사용 약관 및 개인 정보 보호 정책 및 현지 법률 및 규정을 준수…...
2024年github开源top100中文
2024年github开源top100中文 动动美丽的小指头点个赞呗,感谢啦!💕💕💕😘😘😘 本文由Butterfly一键发布工具发布 语言star项目名称描述Python45670xai-org/grok-1Grok开源发布Ruby260…...
回收站删除的文件在哪里?专业恢复方法分享(最新版)
“我很想知道我从回收站删除的文件被保存在哪里了呢?我刚刚不小心清空了回收站,现在想将它们恢复,应该怎么操作呢?谁能教教我怎么从回收站恢复文件?” 回收站,作为Windows操作系统中的一个重要组件…...
什么是工时管理软件?
简而言之,工时管理软件是一种可以帮助管理者跟踪企业员工在项目和任务上花费的时间的软件。然而,工时管理软件不仅是一种收集信息的工具,它还是一种解决方案,使企业能够处理和优化不同的流程和活动,例如工资单、项目预…...
一文解析智慧城市,人工智能技术将成“智”理主要手段
长期以来,有关智慧城市的讨论主要围绕在技术进步方面,如自动化、人工智能、数据的公开以及将更多的传感器嵌入城市以使其更加智能化。实际上,智慧城市是一个关于未来的设想,其重要原因在于城市中存在各种基础设施、政治、地理、财…...
SQLBolt,一个练习SQL的宝藏网站
知乎上有人问学SQL有什么好的网站,这可太多了。 我之前学习SQL买了本SQL学习指南,把语法从头到尾看了个遍,但仅仅是心里有数的程度,后来进公司大量的写代码跑数,才算真真摸透了SQL,知道怎么调优才能最大化…...
TikTok防关联引流系统:全球多账号运营的终极解决方案
tiktok防关联引流系统介绍,tiktok防关联系统是基于tiktok生态研发的效率工具,帮你快速实现tiktok全球多账号运营,系统配备了性能强劲的安卓,防关联智能终端,可一建创建全球多国手机环境,完美满足各类app软件…...
卷积神经网络(CNN)的数学原理解析
文章目录 前言 1、介绍 2、数字图像的数据结构 3、卷积 4、Valid 和 Same 卷积 5、步幅卷积 6、过渡到三维 7、卷积层 8、连接剪枝和参数共享 9、卷积反向传播 10、池化层 11、池化层反向传播 前言 本篇主要分享卷积神经网络(CNN)的数学原理解析…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
