Python
教程
深浅复制
- **直接赋值:**其实就是对象的引用(别名)。
- **浅拷贝(copy):**拷贝父对象,不会拷贝对象的内部的子对象。
- 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
形象理解:
大箱子(容器)里面嵌套了许多小箱子,小箱子里面又有小箱子,最内部箱子里面有值
- 直接赋值:给最外部大箱子重新贴了一个tag
- 浅拷贝(copy):重新换了一个外部大箱子
- 深拷贝(deepcopy):全部箱子对应新换,值不变
一句话核心区别:
引用就是用别名,浅拷贝是复制一层壳,深拷贝是复制全套内外。
Python 字典(Dictionary) copy()方法
|
|
f-string 格式化
Python格式化字符串f-string概览_sunxb10的博客-CSDN博客_f string
语法:
{content:format}
进制转换
Python 二进制,十进制,十六进制转换_kunpengku的博客-CSDN博客_python 二进制转十进制
转十进制:int(x,[base])
转二进制:bin()、
转8进制:oct()
转16进制:hex()
内置函数:
https://www.runoob.com/python3/python3-built-in-functions.html
https://www.runoob.com/python3/python3-built-in-functions.html
任意键继续/暂停
|
|
None值
is表示的是对象标识符,用来检查对象的标识符是否一致,即两个对象在内存中的地址是否一致。在使用 a is b
的时候,相当于id(a)==id(b)
。
因为None在Python里是个单例对象,一个变量如果是None,它一定和None指向同一个内存地址。
逻辑判断
[Python]万物皆可Bool——Python的隐式布尔值和真值测试
Python提供了一个简单有效的真值测试机制,来得到一切对象的隐式布尔值。该机制借助两个魔术方法__bool__
和__len__
,如果我们希望给自定义的类赋予符合一定逻辑的布尔值,可以通过定义这两个魔术方法来完成。Python也提供了一个内置函数bool
,使得对象的隐式布尔值可以通过该函数显式计算得到,而不一定只能用于if
,while
等过程。
任何对象都可以在if
,while
语句或and
,or
等布尔操作符中进行真值测试(Truth Value Testing),测试的结果默认是True
,除非一下两种情况的任何一种发生:
- 该对象的类定义了
__bool__
函数,且该函数返回False
- 该对象的类定义了
__len__
函数,且该函数返回0
另外,以下内置对象被认为是False
:
- 常量
None
和False
- 值为0的数值类型:
0
,0.0
,0j
,Decimal(0)
,Feaction(0, 1)
- 空的序列和容器类型:
''
,()
,[]
,{}
,set()
,range(0)
类型 | False | True |
---|---|---|
布尔 | False(与0等价) | True(与1等价) |
数值 | 0, 0.0 | 非零的数值 |
字符串 | ‘’, “"(空字符串) | 非空字符串 |
容器 | [], (), {}, set() | 至少有一个元素的容器对象 |
None | None | 非None对象 |
时间处理
Python时间日期格式化之time与datetime模块总结 - 奥辰 - 博客园
|
|
计时器
|
|
glob
https://rgb-24bit.github.io/blog/2018/glob.html
|
|
排序
|
|
序列类型
range to list
|
|
列表list
转字符串
在 Python 中将 列表转换为字符串 是常见操作,下面是各种方法的总结(适用于不同场景)👇
✅ 方法 1:join()
— 最常用方式(适用于字符串列表)
|
|
🔹说明:
join()
只能用于字符串组成的列表。- 可自定义分隔符(如
', '
,'\n'
,''
等)。
✅ 方法 2:map()
+ join()
— 用于非字符串列表
|
|
🔹说明:
map(str, lst)
把列表中所有元素转成字符串。- 比直接遍历更优雅、性能更好。
✅ 方法 3:str()
直接转(带括号和逗号)
|
|
🔹说明:
- 不推荐用于拼接字符串,仅用于查看或调试。
- 输出带有中括号
[]
和引号'a'
。
✅ 方法 4:list comprehension
自定义格式化
|
|
🎯 总结对比:
方法 | 适用类型 | 输出格式 | 推荐程度 |
---|---|---|---|
', '.join(lst) | 字符串列表 | 无括号 | ✅✅✅ |
', '.join(map(str, lst)) | 任意列表 | 无括号 | ✅✅✅ |
str(lst) | 任意列表 | 有括号 | ⚠️ 一般 |
列表推导式 + join | 任意列表 | 自定义格式 | ✅✅ |
列表生成式
|
|
Python列表解析配合if else_python 列表生成式 if else-CSDN博客
字典
创建字典
python创建字典(dict)的几种方法(详细版)_python如何构建字典-CSDN博客
python字典的键值对互换 - Little_Raccoon - 博客园
|
|
星号*用法
https://www.jianshu.com/p/a159ea1dbccc
*位置参数
**关键字参数
调用函数时使用* **
test(args) 的作用其实就是把序列 args 中的每个元素,当作位置参数传进去。比如上面这个代码,如果 args 等于 (1,2,3) ,那么这个代码就等价于 test(1, 2, 3) 。
test(kwargs) 的作用则是把字典 kwargs 变成关键字参数传递。比如上面这个代码,如果 kwargs 等于 {‘a’:1,‘b’:2,‘c’:3} ,那这个代码就等价于 test(a=1,b=2,c=3) 。
print打印
换行打印
print(*sys.path, sep='\n')
print('\n'.join(files))
实时缓冲问题
这种现象通常是由于 Python 解释器的缓冲机制所导致的。在 PyCharm 里,它或许会对输出进行特殊处理,从而让日志能够实时显示;而在终端中,Python 解释器默认采用行缓冲或者块缓冲,这就使得日志要等到缓冲区满或者程序结束时才会被打印出来。下面为你介绍几种解决办法。
方法一:在脚本中设置 stdout
为无缓冲模式
你可以在 Python 脚本里把 sys.stdout
设置为无缓冲模式,这样就能让日志实时输出。
|
|
方法二:使用 u
选项运行 Python 脚本
在调用 subprocess.Popen()
时,使用 -u
选项来运行 Python 脚本,这个选项的作用是强制使用无缓冲的标准输出。
|
|
方法三:在终端中设置环境变量
在终端里设置 PYTHONUNBUFFERED
环境变量为 1
,这样可以让 Python 解释器使用无缓冲的标准输出。
|
|
代码解释
- 方法一:通过
sys.stdout.reconfigure(buffer=0)
把stdout
设置为无缓冲模式,保证每次调用print()
函数时日志都能立即输出。 - 方法二:在
subprocess.Popen()
中使用u
选项,让 Python 解释器以无缓冲模式运行脚本,然后实时读取并打印脚本的输出。 - 方法三:设置
PYTHONUNBUFFERED
环境变量为1
,全局生效,使得所有 Python 脚本都以无缓冲模式运行。
你可以根据具体情况选择合适的方法来解决日志不能实时输出的问题。
argparse
常用参数选项
type
:指定参数类型(如int
,float
,str
等)。default
:指定默认值。help
:参数的帮助信息。required
:是否必须提供(针对可选参数)。required=True
choices
:限制参数的取值范围。choices=['train', 'test']
nargs
:指定参数的数量(如?最多一个, *不限, +最少一个
等)。action
:指定参数的行为(如store_true
,store_false,出现就选取默认值
)
|
|
RawTextHelpFormatter
1. 为什么使用 RawTextHelpFormatter
?
- 保留格式:默认的
HelpFormatter
会自动换行并调整文本格式,而RawTextHelpFormatter
会保留帮助文本的原始格式。 - 复杂帮助信息:当帮助信息包含多行文本、表格、代码块等复杂内容时,使用
RawTextHelpFormatter
可以更好地控制显示效果。
|
|
迭代器、生成器
迭代器是一种按照从前向后的顺序依次访问元素的对象,访问时不能回退;
生成器是一个返回迭代器的函数,包含yield关键字,可以在迭代过程中逐步产生值,其遇到yield语句会暂停并return当前结果,函数会从上次暂停的地方继续执行。
迭代器
迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
生成器
生成器是 Python 中的一种特殊类型的迭代器,允许你使用 yield
关键字来逐步生成值,而不是一次性返回所有值。这使得生成器在处理大数据集时非常高效,因为它们可以按需生成数据,而不是在内存中存储所有数据。
生成器的基本特性
- 延迟计算:生成器的值在需要时才计算,避免了一次性占用大量内存。
- 可迭代性:生成器对象可以用在
for
循环中,也可以转换为列表、元组等可迭代对象。 - 状态保持:每次调用生成器的
__next__()
方法时,生成器会记住上一次的状态(包括局部变量等)。
创建生成器
生成器可以通过以下两种方式创建:
1. 使用生成器函数
生成器函数是一个普通函数,但使用 yield
语句返回值。例如:
python
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
# 使用生成器
for number in count_up_to(5):
print(number)
# print
1
2
3
4
5
闭包
Python 中,闭包(Closure)是一个函数对象,能够“记住”其定义时的作用域,即使在函数的外部被调用时,仍然可以访问那些局部变量。闭包通常用于在函数内部定义另一个函数,并返回该内部函数。
闭包的特点
1. 嵌套作用域
闭包的内部函数能够访问外部函数的变量。即使外部函数已经执行完毕,内部函数依然可以“记住”这些变量的状态。
2. 函数对象
闭包是一个函数对象,它可以被调用并返回其值。这使得闭包可以被赋值给变量、作为参数传递或作为返回值返回。
3. 保持状态
闭包可以用来维护状态。通过外部函数的参数,闭包能够记住外部函数的局部变量的值,适合于实现一些需要状态保持的功能。
4. 避免全局变量
闭包可以在不使用全局变量的情况下,封装一些数据和状态。这增强了代码的封装性和可维护性。
5. 延迟计算
闭包可以用于延迟计算,因为它们可以在需要时才被调用。你可以创建一个闭包,在之后的时间点执行,而不是立即执行。
创建闭包的步骤
- 定义一个外部函数。
- 在外部函数中定义一个内部函数。
- 在外部函数中返回内部函数。
|
|
装饰器
https://www.runoob.com/w3cnote/python-func-decorators.html
https://foofish.net/python-decorator.html
https://blog.csdn.net/jpch89/article/details/84026130
随机数生成
在python中,有两个模块可以产生随机数:
- python自带random包: 提供一些基本的随机数产生函数,可满足基本需要
2. numpy.random:提供一些产生随机数的高级函数,满足高级需求
区别总结
- 功能范围:
random
模块提供的功能较为基础,而np.random
提供的功能更强大,特别是在生成多维随机数方面更为高效和灵活。 - 速度和效率:
np.random
通常比random
快,尤其是在生成大规模随机数时,因为 NumPy 是用 C 语言实现的,性能更高。 - 多维数组:
np.random
更适合科学计算,可直接生成多维数组的随机数,而random
主要用于一维随机数生成。
本文先分别介绍这两个包的常用函数,文末将总结一下两个包的区别。
Python随机数小结——random和np.random的区别与联系_Zhang_Raymond的博客-CSDN博客
Python——随机数生成模块numpy.random - 怪猫佐良 - 博客园
np.random.seed(1234)
np.random简单随机数
函数名称 | 函数功能 | 参数说明 |
---|---|---|
rand(d0, d1, …, dn) | 产生均匀分布的随机数 | dn为第n维数据的维度 |
randn(d0, d1, …, dn) | 产生标准正态分布随机数 | dn为第n维数据的维度 |
randint(low[, high, size, dtype]) | 产生随机整数 | low:最小值;high:最大值;size:数据个数 |
random_sample([size]) | 在[0,1)内产生随机数 | size:随机数的shape,可以为元祖或者列表,[2,3]表示2维随机数,维度为(2,3) |
random([size]) | 同random_sample([size]) | 同random_sample([size]) |
ranf([size]) | 同random_sample([size]) | 同random_sample([size]) |
sample([size])) | 同random_sample([size]) | 同random_sample([size]) |
choice(a[, size, replace, p]) | 从a中随机选择指定数据 | a:1维数组 size:返回数据形状 |
bytes(length) | 返回随机位 | length:位的长度 |
数据统计
|
|
图像显示
解决plt.imshow显示cv2.imread读取的图像有色差发蓝的四种方法问题_python_脚本之家
python bgr转rgb,Python OpenCV-imshow不需要从BGR转换为RGB
下划线
python中的下划线”_“讲解_dfd中的下划线_随心1993的博客-CSDN博客
自定义的最好不要用双下划线
环境管理
|
|
在PyCharm中,你可以使用一些方法来判断当前是否处于Debug模式还是Run模式。以下是一些可能的方法:
|
|
使用 PyCharm 特定的环境变量:
PyCharm 在运行和调试时会设置一些环境变量,你可以利用这些环境变量来判断当前的模式。
例如,可以检查 PYCHARM_HOSTED 是否存在:
|
|
使用 debug 变量:
在 Python 中,存在一个特殊的 debug 变量,其在调试模式下为 True,在正常运行模式下为 False。在PyCharm的Debug模式下,debug 通常为 True。
|
|
这些方法中,第一和第二种方法是比较通用的,可以在其他IDE或环境中使用。第三种方法则是一种 Python 内建的方式,但需要注意的是,在某些特殊情况下可能会有异常。选择适合你项目和偏好的方法。
切换工作目录
在 Python 脚本中,若要将脚本的当前工作目录(Current Working Directory,CWD)切换为脚本所在的目录,可借助 os
或 pathlib
模块来实现。下面为你分别介绍这两种模块的实现方式。
使用 os
模块
os
模块提供了与操作系统进行交互的功能,可用于获取脚本的路径并切换工作目录。示例代码如下:
|
|
代码解释
- 获取脚本所在的目录:
os.path.abspath(__file__)
:__file__
是 Python 内置变量,代表当前脚本的文件名。os.path.abspath()
函数将其转换为绝对路径。os.path.dirname()
:该函数用于获取文件所在的目录路径。
- 切换工作目录:
os.chdir()
函数用于将当前工作目录切换到指定的目录。 - 验证切换结果:
os.getcwd()
函数用于获取当前工作目录,并将其打印输出。
使用 pathlib
模块
pathlib
模块是 Python 3.4 及以上版本引入的面向对象的文件路径操作模块,使用起来更加简洁和直观。示例代码如下:
|
|
代码解释
- 获取脚本所在的目录:
Path(__file__)
:创建一个Path
对象,表示当前脚本的路径。resolve()
:将相对路径转换为绝对路径。parent
:获取该路径的父目录。
- 切换工作目录:同样使用
os.chdir()
函数将当前工作目录切换到指定的目录。 - 验证切换结果:使用
os.getcwd()
函数获取并打印当前工作目录。
通过以上两种方法,你可以将 Python 脚本的当前工作目录切换为脚本所在的目录。
文件处理
Python字典保存(方便读取)_python 保存dict_风雨潇潇一书生的博客-CSDN博客
Object of type ’ndarray’ is not JSON serializable_AI视觉网奇的博客-CSDN博客
shutil.copy2(src, dst) 复制文件,保留元数据
路径处理
【python3基础】相对路径,‘/’,‘./’,‘../’ - wuliytTaotao - 博客园
Python - 获取当前目录/上级目录/上上级目录_python 如何访问上级目录_wise哲的博客-CSDN博客
|
|
面向对象
Python 中 property() 函数及 @property 装饰器的使用-CSDN博客
获取函数名
获取函数的名称
|
|
|
|
多进程
使用 multiprocessing 模块的 Pool 类的 apply 方法时,可以通过 get 方法获取子进程的返回值。以下是一个示例:
|
|
在这个例子中,apply 方法会将 square 函数应用于参数 5,并返回结果。使用 get 方法获取返回值。
需要注意的是,apply 方法是阻塞的,即程序会等待子进程执行完毕并返回结果后才会继续执行。如果要并行执行多个任务并获取返回值,建议使用 map 方法。如果需要使用 apply,可以考虑使用 AsyncResult 对象来异步获取返回值。例如:
|
|
这样可以实现异步获取返回值,而不会阻塞主进程。
apply_async 方法的执行顺序是不确定的,因为它取决于子进程的启动和执行速度。不同的子进程可能以不同的顺序完成执行。
因此,不能假设子进程的返回值的顺序与调用 apply_async 的顺序相同。
子进程
推荐
subprocess.run() # 阻塞处理
subprocess.Popen() # 非阻塞处理
方法对比
gpt
在 Python 中,subprocess
模块是执行 shell 命令的主要方式。常用方法包括:
方法 | 适用场景 | 是否支持管道 | 是否获取返回值 | 是否阻塞 |
---|---|---|---|---|
subprocess.run() | 简单命令,需等待执行完成 | ❌ | ✅ | ✅ |
subprocess.call() | 简单命令,仅返回状态码 | ❌ | ❌ | ✅ |
subprocess.Popen() | 需要实时交互或流式处理 | ✅ | ✅(communicate() 获取) | ❌ |
os.system() | 执行简单 shell 命令 | ❌ | ❌(仅返回状态码) | ✅ |
commands.getoutput() (Python 2) | 仅 Python 2,可获取输出 | ❌ | ✅ | ✅ |
1. subprocess.run()
(推荐)
Python 3.5+ 推荐使用 subprocess.run()
,它可执行 shell 命令并返回 CompletedProcess
对象。
示例
|
|
优点
- 支持获取返回值 (
stdout
,stderr
) - 自动等待进程完成
- 支持
capture_output=True
直接获取输出
2. subprocess.call()
类似 run()
,但只返回状态码,不返回输出。
示例
|
|
适用场景
- 仅需要知道命令是否成功,而不关心输出。
3. subprocess.Popen()
(适用于交互或流式处理)
适用于长时间运行的命令、实时读取输出或需要交互的情况。
示例
|
|
优点
- 支持实时读取输出
- 不阻塞主线程
- 支持
communicate()
进行交互
适用场景
- 长时间运行的进程
- 实时获取输出
- 需要进程间通信
4. os.system()
(不推荐)
执行 shell 命令,但无法获取输出,只返回退出状态码。
示例
|
|
缺点
- 无法捕获输出
- 不支持管道
- 安全性较差(可能导致 Shell 注入)
5. commands.getoutput()
(仅 Python 2)
Python 3 已移除,等效于 subprocess.check_output()
。
示例(Python 2)
|
|
对比总结
方法 | 获取输出 | 获取状态码 | 是否推荐 |
---|---|---|---|
subprocess.run() | ✅(可选 capture_output=True ) | ✅ | ✅ 推荐 |
subprocess.call() | ❌ | ✅ | ❌ 仅适用于简单情况 |
subprocess.Popen() | ✅(stdout=PIPE ) | ✅ | ✅ 适用于流式处理 |
os.system() | ❌ | ✅ | ❌ 不推荐 |
commands.getoutput() (Python 2) | ✅ | ❌ | ❌ 仅适用于 Python 2 |
🔹 推荐使用 subprocess.run()
,如果需要流式处理则使用 subprocess.Popen()
。
qwen
以下是 Python 中执行 Shell 命令的子进程方法对比,包含使用场景、优缺点及代码示例:
1. os.system()
特点
- 简单直接:适合执行单条命令
- 输出到终端:结果直接打印到控制台,无法捕获
- 返回状态码:返回命令的退出状态码
- 安全隐患:直接拼接字符串易受 Shell 注入攻击
示例
|
|
适用场景
- 快速执行简单命令(如脚本启动)
- 无需捕获输出的场景
2. subprocess.run()
(Python 3.5+ 推荐)
特点
- 功能全面:支持超时、输入/输出捕获、错误检查
- 返回
CompletedProcess
对象:包含返回码、输出等信息 - 默认阻塞:等待命令执行完成
- 安全参数传递:通过列表形式避免 Shell 注入
示例
|
|
适用场景
- 需要捕获输出或检查错误的场景
- 需要超时控制的长时间任务
3. subprocess.Popen
(高级控制)
特点
- 非阻塞执行:可通过
communicate()
控制阻塞行为 - 灵活的 I/O 管理:支持管道、重定向、实时读取输出
- 复杂流程:适合需要多进程交互的场景(如管道)
示例
|
|
适用场景
- 实时处理命令输出(如日志监控)
- 复杂进程交互(如管道、多命令组合)
4. subprocess.call()
/ check_call()
特点
- 简单封装:
call()
:返回状态码check_call()
:状态码非零时抛出异常
- 阻塞执行:等待命令完成
示例
|
|
适用场景
- 需要简单判断命令是否成功的场景
5. sh
模块(第三方库)
特点
- 语法简洁:提供类似 Shell 的调用方式
- 自动解码:输出直接返回字符串
- 链式调用:支持管道操作
示例
|
|
适用场景
- 快速开发脚本(需安装
sh
库:pip install sh
)
对比总结表
方法 | 输出捕获 | 实时处理 | 超时控制 | 安全性 | 复杂度 | 推荐场景 |
---|---|---|---|---|---|---|
os.system() | ❌ | ❌ | ❌ | 低(易注入) | 简单 | 快速执行简单命令 |
subprocess.run() | ✅ | ❌ | ✅ | 高 | 中等 | 需要捕获输出的标准任务 |
subprocess.Popen | ✅ | ✅ | ✅ | 高 | 复杂 | 实时交互或复杂进程管理 |
subprocess.call() | ❌ | ❌ | ❌ | 中 | 简单 | 仅需状态码的简单命令 |
sh 模块 | ✅ | ✅ | ❌ | 中 | 简单 | 快速脚本开发(第三方依赖) |
安全最佳实践
避免
shell=True
:防止命令注入攻击1 2 3 4 5
# 不安全写法 subprocess.run("ls " + user_input, shell=True) # 安全写法 subprocess.run(["ls", user_input])
参数列表化:始终将命令拆分为参数列表
1 2
from shlex import split subprocess.run(split("ping -c 4 google.com"))
限制权限:通过
preexec_fn
或用户组限制子进程权限(Unix)1 2 3 4
subprocess.run( ["dangerous_cmd"], preexec_fn=lambda: os.setuid(1000) # 降权运行 )
根据需求选择合适的方法,推荐优先使用 subprocess.run()
或 subprocess.Popen
以获得更好的控制和安全性。
run详细用法
subprocess.run()
是 Python subprocess
模块中的一个常用方法,用于执行 shell 命令,并等待命令完成后返回结果。
1. subprocess.run()
语法
|
|
2. 详细参数说明
参数 | 说明 | 默认值 |
---|---|---|
args | 需要执行的命令(字符串或列表) | 必须提供 |
stdin | 指定子进程的标准输入(如 subprocess.PIPE ) | None |
input | 传递给子进程的输入数据(如字符串或字节流) | None |
stdout | 指定子进程的标准输出(如 subprocess.PIPE 捕获输出) | None |
stderr | 指定子进程的标准错误输出 | None |
capture_output | 如果为 True ,等价于 stdout=subprocess.PIPE, stderr=subprocess.PIPE | False |
shell | 是否通过 shell 运行命令(如 bash ) | False |
cwd | 指定运行命令的工作目录 | None |
timeout | 超时时间(秒),超过时间会抛出 subprocess.TimeoutExpired 异常 | None |
check | 是否检查返回码,如果非 0 则抛出 subprocess.CalledProcessError | False |
encoding | 指定输出的编码(如 encoding="utf-8" ) | None |
text | True 时等价于 encoding="utf-8" ,返回字符串而非字节流 | None |
3. subprocess.run()
示例
(1)最基本的使用
|
|
(2)捕获标准输出
|
|
等价于:
|
|
(3)使用 shell=True
|
|
⚠️ 注意:
shell=True
适用于运行复杂的 shell 命令,如ls *.txt | grep "data"
。- 不推荐对不受信任的输入使用
shell=True
,因为可能导致 命令注入(安全风险)。
(4)改变工作目录
|
|
(5)传递输入
|
|
(6)超时控制
|
|
(7)错误处理
|
|
4. subprocess.run()
返回对象
subprocess.run()
返回 CompletedProcess
对象,该对象包含以下信息:
|
|
5. subprocess.run()
vs subprocess.Popen()
方法 | 是否阻塞 | 是否可交互 | 适用场景 |
---|---|---|---|
subprocess.run() | ✅ 阻塞 | ❌ 不可交互 | 运行简单命令,获取输出 |
subprocess.Popen() | ❌ 非阻塞 | ✅ 可交互 | 需要实时获取进程状态、交互式命令 |
总结
subprocess.run()
适用于运行命令并等待结果- 可以捕获输出、设置超时、改变工作目录
- 对于交互式进程或长期运行的进程,使用
subprocess.Popen()
🚀 推荐使用 capture_output=True, text=True
获取标准输出
|
|
Popen详细用法
subprocess.Popen()
是 Python 用于创建子进程的强大工具,常用于执行外部命令或程序。以下是 Popen
的常用参数及其作用:
1. subprocess.Popen()
基本格式
|
|
2. 主要参数解析
参数 | 作用 |
---|---|
args | 要执行的命令,可以是字符串(shell=True 时)或列表(shell=False 时) |
shell | 是否使用 shell 解析命令(默认为 False )。True 时支持 && 、` |
stdout | 标准输出,可设为 subprocess.PIPE (捕获输出)、None (直接显示)、文件对象 |
stderr | 标准错误输出,同 stdout |
stdin | 标准输入,用于传递输入数据,如 subprocess.PIPE |
cwd | 指定子进程的 工作目录 |
env | 环境变量,可传入 dict 来修改默认环境 |
text / universal_newlines | True 时,stdout 和 stderr 返回 字符串(默认是字节流) |
encoding | 指定编码,如 utf-8 |
bufsize | 缓冲区大小,默认为 -1 (系统默认),0 (无缓冲),1 (行缓冲) |
executable | 指定要执行的二进制文件路径 |
close_fds | True 时关闭子进程的文件描述符(Windows 不支持) |
3. args
参数示例
✅ 推荐(shell=False
,列表方式)
|
|
优点:
- 更安全(避免
shell
命令注入) - 自动处理参数(如空格、特殊字符)
⚠️ shell=True
(字符串方式)
|
|
适用于:
- 需要执行 多个命令(如
ls -l | grep py
) - 需要使用 shell 变量(如
$HOME
)
注意:⚠ shell=True
可能导致 命令注入风险,特别是如果 args
是用户输入的内容。
4. stdout
和 stderr
捕获
|
|
stdout=subprocess.PIPE
:捕获子进程的 标准输出stderr=subprocess.PIPE
:捕获子进程的 错误输出text=True
:自动解码为字符串(默认是字节流)
5. 读取子进程的输出
(1) process.communicate()
方式
|
|
适用于:
- 一次性读取完整输出
- 阻塞 等待子进程完成
(2) process.poll()
轮询检查状态
|
|
适用于:
- 需要在子进程运行时执行其他任务
- 非阻塞方式
(3) process.wait()
等待子进程结束
|
|
适用于:
- 阻塞 直到子进程完成
- 返回 子进程的退出码
(4) 逐行读取子进程输出
|
|
适用于:
- 实时获取子进程输出
- 适合 长期运行的进程
6. stdin
传入数据
|
|
适用于:
- 需要给子进程提供输入数据
7. 终止子进程
|
|
terminate()
:发送 SIGTERM 终止kill()
:发送 SIGKILL 强制终止wait()
:确保进程完全退出
8. 运行带环境变量的命令
|
|
适用于:
- 需要给子进程提供特定的环境变量
9. cwd
指定工作目录
|
|
适用于:
- 需要在特定目录下执行命令
10. 等待多个子进程
|
|
适用于:
- 启动多个子进程,并等待它们全部结束
总结
功能 | 关键参数 | 代码示例 |
---|---|---|
执行命令 | args | subprocess.Popen(["ls", "-l"]) |
使用 shell | shell=True | subprocess.Popen("ls -l", shell=True) |
获取输出 | stdout=subprocess.PIPE | out, _ = process.communicate() |
实时读取输出 | for line in process.stdout | 见 逐行读取 示例 |
输入数据 | stdin=subprocess.PIPE | process.communicate("data") |
检查进程状态 | process.poll() | 见 轮询检查 示例 |
终止进程 | process.terminate() | process.kill() |
工作目录 | cwd="/tmp" | subprocess.Popen(["ls"], cwd="/tmp") |
环境变量 | env={"MY_VAR": "123"} | subprocess.Popen(["printenv", "MY_VAR"], env=env) |
📌 推荐:
- 执行外部命令时尽量使用
shell=False
(避免安全风险) - 需要返回数据时,
stdout=subprocess.PIPE, text=True
process.communicate()
适用于一次性读取,for line in process.stdout
适用于实时读取
这样,你就可以灵活使用 subprocess.Popen()
处理子进程了!🚀
p.communicate() v.s. p.poll()
p.communicate()
和 p.poll()
都用于处理 Python subprocess.Popen()
进程,但它们的作用和适用场景不同:
方法 | 作用 | 是否阻塞 | 返回值 | 适用场景 |
---|---|---|---|---|
p.communicate() | 读取标准输出/标准错误并等待进程结束 | ✅ 阻塞 | (stdout, stderr) | 需要获取完整输出,并等待进程结束 |
p.poll() | 检查进程是否结束,不阻塞 | ❌ 非阻塞 | None (进行中)或退出码 | 需要非阻塞地检查进程状态 |
1. p.communicate()
p.communicate()
适用于一次性获取子进程的完整输出,同时等待进程结束。
示例
|
|
特点
- 会阻塞(等待进程结束)
- 一次性读取
stdout
和stderr
- 防止死锁(避免
stdout
或stderr
缓冲区溢出)
2. p.poll()
p.poll()
适用于非阻塞地检查进程是否结束,它不会等待进程结束,而是立即返回进程状态。
示例
|
|
特点
- 不会阻塞
- 仅返回进程状态
- 如果进程结束,返回退出码;否则返回
None
3. 何时用 p.communicate()
,何时用 p.poll()
?
- 如果你需要获取进程输出并等待进程结束,用
p.communicate()
。 - 如果你只想检查进程是否结束(但不阻塞),用
p.poll()
。
4. p.communicate()
vs p.poll()
综合示例
如果需要非阻塞地检查进程状态,并在进程结束后获取输出,可以组合使用:
|
|
总结
方法 | 作用 | 是否阻塞 | 返回值 | 适用场景 |
---|---|---|---|---|
p.communicate() | 读取标准输出/错误并等待进程结束 | ✅ 阻塞 | (stdout, stderr) | 需要获取完整输出,并等待进程结束 |
p.poll() | 检查进程是否结束 | ❌ 非阻塞 | None (运行中)或退出码 | 需要非阻塞检查进程状态 |
🚀 最佳实践
p.poll()
适用于需要非阻塞检查进程是否结束p.communicate()
适用于获取子进程的输出并等待完成- 如果进程可能输出大量数据,建议使用
p.communicate()
避免缓冲区溢出
command lines
在 Python 中,判断脚本是否在终端中执行的方式可以是通过检查标准输入、输出是否连接到终端。
可以使用 sys.stdin.isatty() 来检查标准输入是否连接到终端,以及 sys.stdout.isatty() 来检查标准输出是否连接到终端。
以下是一个示例:
|
|
在终端中执行时,sys.stdin.isatty() 和 sys.stdout.isatty() 通常都返回 True,而在非终端环境中返回 False。
请注意,这种方法可能不是绝对可靠的,因为在一些特殊情况下,即使在终端中运行,也可能返回 False。这主要取决于操作系统和环境。
open source
GitHub Top 10 + Python 开源项目(2021版
强烈推荐:GitHub 上开源的 13 个 Python 资源 - 掘金
推荐11个值得研究学习的Python开源项目(从入门到python高级开发) - GlaryJoker - 博客园
noqa
在 Python 深度学习代码中,# noqa
是 Flake8 代码检查工具的一种指令,用于忽略特定代码行的警告或错误。
📌 # noqa
作用
# noqa
全称是 “No Quality Assurance”,主要用于**抑制静态代码分析工具(如 Flake8、pylint)**检测到的错误。例如,在深度学习代码中,可能遇到以下情况:
1️⃣ 忽略未使用的导入
在 PyTorch 或 TensorFlow 代码中,某些导入可能仅用于装饰器或动态加载,但 Flake8 可能会报 F401 'XXX' imported but unused
:
|
|
✅ 这样可以防止 Flake8 报告未使用的导入。
2️⃣ 忽略长行超出 79/88 字符限制
如果代码行超过 PEP8 规定的 79 或 88 个字符(比如复杂的神经网络定义),可以使用 # noqa
:
|
|
3️⃣ 忽略 E402
(导入不在文件顶部)
有时为了兼容 TensorFlow 或 PyTorch 设备初始化,可能会在导入之前进行某些设置:
|
|
✅ # noqa: E402
让 Flake8 忽略导入不在顶部的错误。
💡 # noqa
在深度学习中的常见场景
场景 | noqa 代码示例 |
---|---|
忽略未使用的导入 | import torch # noqa: F401 |
忽略长行警告 | model = nn.Sequential(...) # noqa |
忽略导入顺序警告 | import torch # noqa: E402 |
忽略变量未使用 | _ = torch.randn(1) # noqa: F841 |
🚀 总结
# noqa
用于忽略 Flake8/Pylint 代码检查。- 常见于深度学习代码,用于跳过未使用的导入(如
torch
,tensorflow
)、长行警告等。 - 可以指定错误代码,如
# noqa: F401
仅忽略 未使用导入,避免忽略其他潜在错误。
如果你在深度学习项目中看到 # noqa
,通常是为了让代码格式检查工具不报错,而不会影响代码的运行。 🚀
PYTHONPATH
在 Shell 中执行 Python 脚本时,如果遇到 ModuleNotFoundError
,很可能是 PYTHONPATH
没有正确设置,导致 Python 无法找到模块或包。下面是一些常见的解决方法:
1. 直接在 Shell 中临时设置 PYTHONPATH
如果你的 Python 模块位于 /home/user/my_project/
,那么可以在运行 Python 脚本时手动指定 PYTHONPATH
:
|
|
或者直接一行执行:
|
|
适用于: 临时解决路径问题,每次新开终端都需要重新设置。
2. 在 Python 代码中手动添加 sys.path
如果你不想手动设置环境变量,可以在 your_script.py
代码顶部添加:
|
|
适用于: 代码可控的情况,适用于多个环境,但需要手动修改代码。
3. 直接使用当前工作目录
如果你希望 Python 搜索当前目录,可以使用:
|
|
或者:
|
|
适用于: 代码和模块都在当前目录及其子目录下。
4. 在 .bashrc
/ .bash_profile
/ .zshrc
中永久设置 PYTHONPATH
如果 ModuleNotFoundError
频繁发生,可以把 PYTHONPATH
添加到 ~/.bashrc
或 ~/.zshrc
:
|
|
对于 zsh
(Mac 或部分 Linux 发行版):
|
|
适用于: 需要长期使用某个 Python 项目,不想每次手动设置 PYTHONPATH
。
5. 确保 python
解释器正确
有时 ModuleNotFoundError
可能是因为使用了错误的 Python 解释器。在 Shell 中运行:
|
|
如果你的项目使用虚拟环境(venv / conda),确保你激活了它:
|
|
适用于: 确保使用正确的 Python 解释器。
6. 通过 sys.path
检查 PYTHONPATH
你可以在 Python 交互模式或脚本中执行:
|
|
检查 PYTHONPATH
是否包含你的模块路径。如果缺失,就需要手动添加或设置。
总结
方案 | 适用场景 | 方式 |
---|---|---|
1. export PYTHONPATH=路径 | 临时解决,手动执行 | export 或 PYTHONPATH=xxx python script.py |
2. sys.path.append() | 代码可控,可跨环境 | 在 Python 脚本中添加 |
3. PYTHONPATH=$(pwd) | 当前目录搜索模块 | 适用于单次运行 |
4. ~/.bashrc / ~/.zshrc | 长期使用某路径 | 添加 export PYTHONPATH=xxx |
5. 确保 python 解释器正确 | 可能是错误的 Python 版本 | which python 检查 |
6. 检查 sys.path | 确认 PYTHONPATH 生效 | print(sys.path) |
试试看这些方法,应该可以解决你的 ModuleNotFoundError
问题!🚀
PYTHONPATH vs. sys.path
sys.path
和 PYTHONPATH
都用于 影响 Python 解释器查找模块的路径,但它们的作用方式有所不同。
1. sys.path
是 Python 解释器的模块搜索路径
sys.path
是一个 列表,包含 Python 解释器搜索模块时会参考的所有目录路径。可以在 Python 代码中直接访问和修改:
1 2
import sys print(sys.path) # 输出当前的模块搜索路径
可以手动添加新的搜索路径:
1
sys.path.append('/home/user/my_project') # 追加新的搜索路径
2. PYTHONPATH
是环境变量,影响 sys.path
PYTHONPATH
是 Shell 环境变量,当 Python 启动时,它的值会被自动添加到sys.path
。可以在 Shell 中设置:
1 2
export PYTHONPATH=/home/user/my_project:$PYTHONPATH python script.py
这样,Python 启动时
/home/user/my_project
目录就会被加入sys.path
。
3. sys.path
和 PYTHONPATH
的关系
PYTHONPATH
的值会被 Python 启动时添加到sys.path
。sys.path
包含了默认的 Python 解释器搜索路径(如site-packages
)+PYTHONPATH
变量中的路径 + 运行脚本所在目录。- 修改
sys.path
只影响当前 Python 进程,而PYTHONPATH
影响整个 Shell 会话或系统环境。
4. sys.path
组成
通常,sys.path
包括以下几部分:
- 运行脚本的目录(即
''
或.
)。 - 标准库路径(例如
/usr/lib/python3.x
)。 - site-packages(第三方库的安装目录)。
PYTHONPATH
变量指定的路径(如果有)。- 其他特殊路径(某些 Python 发行版可能会额外添加)。
示例:
|
|
可能输出:
/home/user/my_project
/usr/lib/python3.8
/usr/lib/python3.8/lib-dynload
/usr/local/lib/python3.8/dist-packages
5. 何时使用 PYTHONPATH
vs sys.path
?
方式 | 适用场景 | 作用范围 | 影响时间 |
---|---|---|---|
PYTHONPATH | 设置 Python 解释器的全局搜索路径 | 影响所有 Python 进程 | Python 启动时生效 |
sys.path.append() | 仅影响当前 Python 进程 | 仅当前 Python 运行时 | 运行时动态生效 |
6. 选择使用 PYTHONPATH
还是 sys.path
?
✅ 适合用 PYTHONPATH
:
- 你需要全局(长期)设置 Python 搜索路径(如添加某个项目路径)。
- 你希望在 Shell 中运行 Python 时自动生效(适用于 Docker、远程服务器等)。
- 你在
.bashrc
或.zshrc
中配置了PYTHONPATH
以持久化路径。
✅ 适合用 sys.path
:
- 你只想在 当前 Python 进程 中临时添加路径,而不影响其他 Python 进程。
- 你的脚本会被不同环境运行,手动修改
sys.path.append()
更灵活。 - 你在 Jupyter Notebook 或动态环境中,临时需要额外路径。
7. 示例:两种方式的使用
方式 1:使用 PYTHONPATH
(推荐长期设置)
|
|
这会在 Python 启动时 将 /home/user/my_project
加入 sys.path
。
方式 2:使用 sys.path.append()
(推荐短期/动态修改)
|
|
适用于脚本内部动态修改,而不影响全局 Python 解释器。
总结
PYTHONPATH
影响sys.path
,但sys.path
也可以在运行时动态修改。- 如果要全局长期修改 Python 搜索路径,用
PYTHONPATH
(适用于环境配置)。 - 如果只是临时修改某个 Python 进程的路径,用
sys.path.append()
(适用于代码内部)。
🚀 最佳实践
- 开发环境:在 Shell 设置
PYTHONPATH
,减少sys.path.append()
的使用。 - 脚本运行:如果需要动态修改路径,使用
sys.path.append()
。 - 生产环境:确保
PYTHONPATH
在.bashrc
/.zshrc
中配置好,避免硬编码sys.path.append()
。
这样可以确保 Python 代码在不同环境下都能正确导入模块! ✅
append v.s. insert(0)
在 Python 中,sys.path.append(path)
和 sys.path.insert(0, path)
都用于动态添加模块搜索路径,但它们的优先级和行为有显著差异。以下是详细对比:
1. 核心区别
操作 | 行为 |
---|---|
sys.path.append(path) | 将路径 追加到 sys.path 列表的末尾,优先级最低。 |
sys.path.insert(0, path) | 将路径 插入到 sys.path 列表的最前面,优先级最高。 |
2. 优先级对模块导入的影响
Python 解释器按 sys.path
的顺序搜索模块。假设存在以下路径:
|
|
场景 1:使用 append
|
|
- 新路径
/custom/path
会被添加到列表末尾。 - 优先级:系统路径 > 用户路径。
- 适用场景:希望优先使用系统默认模块,仅在必要时使用自定义路径。
场景 2:使用 insert(0, ...)
|
|
- 新路径
/custom/path
会被插入到列表最前面。 - 优先级:用户路径 > 系统路径。
- 适用场景:希望覆盖系统模块(例如自定义同名模块)或强制优先加载本地代码。
3. 实际示例
假设目录结构如下:
project/
├── main.py
└── my_package/
└── module.py
3.1 使用 append
|
|
- 风险:如果系统中存在同名模块(如
module
),且路径在/path/to/project/my_package
之前,会优先导入系统模块。
3.2 使用 insert
|
|
- 优势:强制优先使用自定义路径中的模块,避免命名冲突。
4. 选择标准
需求 | 推荐方法 |
---|---|
需要覆盖系统或第三方模块 | sys.path.insert(0, path) |
仅补充额外路径(不影响现有逻辑) | sys.path.append(path) |
5. 注意事项
- 临时性:两种方法仅对当前 Python 进程有效,脚本结束后失效。
- 路径冲突:插入高优先级路径可能导致意外覆盖系统模块(例如自定义
os.py
会覆盖标准库os
模块)。 - 可维护性:频繁修改
sys.path
可能导致代码难以维护,建议优先使用虚拟环境(venv
)或安装模块到全局路径。
6. 替代方案
设置
PYTHONPATH
环境变量(全局生效):1
export PYTHONPATH=/custom/path:$PYTHONPATH
使用相对导入:在包内使用
from . import module
。安装模块到全局环境:
1
pip install -e /path/to/package # 开发模式安装
总结
append
:保守添加路径,避免干扰现有模块。insert(0, ...)
:强制优先加载指定路径,适用于调试或覆盖默认行为。
shebang语法
在 Python 中使用子进程时,是否推荐使用 shebang 语法取决于你具体的场景和需求。下面我将详细解释 shebang 语法及其在子进程调用中的适用性。
🔍 什么是 shebang?
shebang(#!) 是一种用于指明脚本文件解释器的语法。例如,#!/usr/bin/env python3
指定该脚本应使用 python3
来执行。它通常放在文件的第一行,作用是告诉操作系统用哪个解释器来执行脚本。
|
|
在 Linux 和 macOS 上,这行会告诉操作系统用 python3
解释器来执行该文件。
🧑💻 shebang 在子进程中的使用场景
1. 子进程执行 Python 脚本
如果你从 Python 中调用另一个 Python 脚本(例如,使用 subprocess
模块),并且你希望该脚本能够正确使用指定的解释器执行,使用 shebang 是非常有意义的。
假设你有一个名为 my_script.py
的 Python 脚本,它的第一行是:
|
|
然后,你在父进程中使用 subprocess.Popen()
调用它:
|
|
为什么需要 shebang?
- 如果你在脚本文件中包含了 shebang,操作系统会根据该行的解释器路径来执行脚本。因此,即使你没有显式指定 Python 解释器,操作系统会自动选择正确的解释器。
- 如果你使用相对路径执行该脚本(例如,
./my_script.py
),而没有提供完整的解释器路径,shebang 会确保脚本被正确执行。
2. Python 子进程调用外部程序
在某些情况下,你可能希望通过 subprocess
调用其他 Python 程序(或者外部程序),而不是执行当前 Python 文件。此时,你可能需要手动指定解释器或使用环境变量。
例如:
|
|
如果你已经在目标脚本中指定了 shebang,你甚至可以跳过显式指定 Python 解释器,如下所示:
|
|
(前提是目标脚本的权限已设置为可执行,并且你在其中包含了正确的 shebang。)
⚠️ 注意事项
确保脚本可执行
使用 shebang 时,目标 Python 脚本需要具有执行权限。你可以使用
chmod
命令设置执行权限:1
chmod +x my_script.py
平台差异
#!/usr/bin/env python3
是跨平台的,但它依赖于操作系统环境,因此在不同平台上可能有不同的行为。确保你的操作系统中正确安装了 Python 解释器。
不使用 shebang 的情况
如果你不打算将脚本作为独立程序运行,而只是作为子进程中的一部分运行,那么可以在
subprocess.Popen()
中明确指定解释器路径,如:1
subprocess.Popen(["python3", "my_script.py"])
这时,shebang 就不再必要了。
✅ 是否推荐使用 shebang?
- 推荐:如果你希望在不同环境下执行 Python 脚本,并且该脚本可能独立运行,使用 shebang 会使得脚本更具可移植性,尤其是在 Linux/macOS 上。
- 不推荐:如果你在 Python 内部通过
subprocess
调用其他 Python 脚本,并且明确知道解释器路径或使用的是虚拟环境,那么不一定需要使用 shebang,可以直接指定解释器路径。
👌 总结
- 如果你的 Python 脚本需要作为独立脚本运行或者有子进程调用,可以使用 shebang 语法。
- 使用
subprocess.Popen()
时,shebang 可以简化命令,自动选择解释器,但也可以直接在父进程中指定 Python 解释器。
包引用
deepseek
在Python项目中,若要在多个子目录及次级子目录中正确导入根目录下的模块,可采用以下步骤:
方法一:动态添加根目录到sys.path
- 在项目根目录创建标记文件(如
.project_root
),以便识别根目录。 - 在各子模块中动态计算根目录路径,并将其添加到
sys.path
中。
示例代码(子模块中):
|
|
方法二:设置PYTHONPATH环境变量
在运行脚本前,将项目根目录添加到PYTHONPATH
中:
|
|
或在脚本中动态设置:
|
|
方法三:使用可编辑安装(推荐用于复杂项目)
- 创建
setup.py
或使用pyproject.toml
配置项目。 - 执行可编辑安装:
|
|
安装后,所有模块可通过包名导入:
|
|
目录结构示例
project_root/
├── .project_root # 标记文件
├── core_utils.py
├── subdir1/
│ ├── __init__.py
│ └── module1.py
└── subdir2/
├── __init__.py
└── nested_subdir/
├── __init__.py
└── module2.py
总结
- 简单项目:使用方法一,动态添加路径,灵活且无需额外配置。
- 团队协作/复杂项目:使用方法三,通过可编辑安装实现标准化导入。
- 快速测试/临时使用:使用方法二,临时设置环境变量。
确保所有子模块在导入前正确设置根目录路径,即可跨目录层级正确导入核心模块。
qwen
要在多个子目录及次级子目录中正确导入根目录下的 core_utils.py
,可以采用以下方法:
方法一:动态添加根目录到 sys.path
在每个需要导入的子模块中,动态计算根目录路径并添加到 sys.path
。例如:
|
|
注意:根据子目录的层级调整 .parent
的次数。例如,若脚本在 app/module2/submodule/
,则可能需要 parent.parent.parent.parent
。
方法二:自动查找根目录
编写函数自动向上查找包含 core_utils.py
的根目录:
|
|
此方法自动定位根目录,无需手动调整路径层级。
方法三:使用相对导入(需包结构)
将项目重构为包结构,确保所有目录包含 __init__.py
文件:
project_root/
├── __init__.py
├── core_utils.py
└── app/
├── __init__.py
├── module1/
│ ├── __init__.py
│ └── script1.py
└── module2/
└── submodule/
└── script2.py
在 script1.py
中使用相对导入:
|
|
方法四:设置环境变量
在运行脚本前设置 PYTHONPATH
:
|
|
或在代码中临时设置:
|
|
方法五:通过包安装(推荐大型项目)
使用 setup.py
或 pyproject.toml
将项目安装为可编辑模式:
|
|
安装命令:
|
|
安装后,可在任意位置导入:
|
|
总结
- 小型项目:使用方法一或二动态添加路径。
- 中大型项目:推荐方法三或五,通过包结构管理导入。
- 灵活运行:方法四设置环境变量。
根据项目需求选择最适合的方案。