在前面的教程中,我们已经介绍了多线程的使用方式,本节教程中我们将详细介绍进程模块。我们可以通俗地理解进程为一个运行起来的程序或者软件,每次启动一个进程,操作系统会自动的为这个程序准备一些必要的资源分配,默认情况下启动一个进程只有一条线程,这个线程就是主线程,线程是依附在进程里面的,没有进程就没有线程,一个进程可以有多个线程。
我们都知道计算机是由硬件和软件组成的。硬件中的CPU是计算机的核心,它承担计算机的所有任务。Python中的多线程无法利用多核CPU优势,如果想要充分地使用多核CPU的资源,大部分情况需要使用多进程。
1、os模块
在前面的第10章Python模块中,我们已经详细介绍了os模块的用法,它提供了与操作系统交互的功能。os模块提供了一些函数来进行进程操作,如创建子进程、获取当前进程ID、结束进程等。
(1)os.getpid()方法
使用os.getpid()方法获取当前进程ID,例如:
动手练一练:
import os
pid = os.getpid()
print("当前进程ID:", pid)
执行以上代码,输出结果为:
当前进程ID: 7312
(2)os.getppid()方法
使用os.getppid()方法获取当前进程的父进程ID,例如:
动手练一练:
import os
ppid = os.getppid()
print("当前进程的父进程ID:", ppid)
执行以上代码,输出结果为:
1488
(3)os.kill()方法
使用os.kill()方法结束进程,例如
动手练一练:
import os
import signal
# 获取当前进程的PID
pid = os.getpid()
# 主动结束指定ID的程序运行
os.kill(pid, signal.SIGTERM)
上面的例子中,os.kill()方法是一种向进程发送信号的方法,可以用来强制结束一个进程的运行。当使用os.kill()方法结束一个进程时,需要指定该进程的PID(进程号),同时需要指定信号类型。这里需要注意的是,直接结束整个程序的运行可能会导致一些未完成的操作无法完成,因此在使用这些指令时需要谨慎。
(4)os.system()方法
os.system()方法是最简单的创建进程的方式,函数只有一个参数,就是要执行的系统命令。例如:
动手练一练:
import os
os.system('cd E:\Pyhint\condition && mkdir aaa')
上面的例子中,cd命令会让程序到达指定文件夹位置“E:\Pyhint\condition”,前提是该文件夹必须存在。最后在指定文件夹内创建一个“aaa”文件夹。从运行结果可以看出,os.system()函数可以执行操作系统的命令,并返回命令的执行状态。此函数的返回值只包含命令执行的状态,0表示成功,非0表示失败。它不包含命令的具体输出内容。
当执行os.system()函数时,它会创建一个子进程在系统上执行相应的命令行。子进程的执行结果不会影响到主进程。使用os.system()执行多条命令时,需要确保这些命令在同一个子进程中运行,例如:os.system('cd E:\Pyhint\condition && mkdir aaa')会执行两条命令,先cd命令会让程序到达指定文件夹,接着在指定文件夹内创建一个“aaa”文件夹,这样才能确保命令按照预期的顺序执行并得到预期的结果。这里需要注意的是,os.system()函数在执行时会阻塞其调用者,等待所启动的命令行程序退出。
在Windows中,可以使用os.system("cls")来清屏。在Windows的cmd终端窗口中,使用os.system("pause")可以实现暂停命令的执行,并提示“请按任意键继续”。
由于os.system()只返回命令的执行状态,而不返回命令的输出结果,因此如果程序需要获取命令运行的输出结果,可以考虑使用其他方法,如subprocess模块。
2、subprocess模块
subprocess模块是Python中一个用于创建和管理子进程(注意不是线程)的模块,它允许你启动新的应用程序,与其进行交互(发送输入数据,访问输出结果),以及获取其执行结果。
通过subprocess模块,我们可以在Python程序中执行外部命令、调用其他外部程序,并与它们进行交互。这使得你可以利用Python的强大功能来自动化和控制其他程序的执行。
(1)subprocess.run()函数的使用
subprocess.run()是Python 3.5及更高版本引入的函数,用于运行外部CMD命令并等待命令完成后,返回一个包含执行结果的subprocess.CompletedProcess对象。例如:
动手练一练:
import subprocess
# 执行CMD命令
result = subprocess.run("dir", shell=True, stdout=subprocess.PIPE, text=True)
# 打印命令输出
print(result.stdout)
以上代码会执行dir命令,它会列出当前Python解释器默认工作目录的所有内容。其中,shell=True参数表示在shell中执行命令,stdout=subprocess.PIPE用于捕获命令的输出,text=True表示输出以文本形式返回。该方法和os模块的os.system('dir')函数执行的效果一模一样。
我们还可以使用subprocess.run()方法运行Python脚本。首先,我们打开目录“Pyhint\condition”(Pyhint编辑器默认的工作路径),在该目录下创建一个简单的Python脚本文件hello.py,打开hello.py文件输入print("Hello World!")并保存。最后打开Pyhint编辑器,在代码框中输入以下内容,并点击运行:
动手练一练:
import subprocess
result = subprocess.run(["python", "hello.py"], capture_output=True, text=True)
print(result.stdout)
执行以上代码,输出结果为:
Hello World!
我们还可以将Python命令传递给subprocess.run()函数,直接从函数中运行Python代码,例如:
动手练一练:
import subprocess
result = subprocess.run(["python", "-c", "print('Hello World')"], capture_output=True, text=True)
print(result.stdout)
执行以上代码,输出结果为:
Hello World
上面的例子中,在参数列表中,第一个元素“python”不能省略,第二个元素“-c”是一个Python标记,允许用户将Python代码以文本形式传递给命令行。第三个元素“print()”是Python命令本身。
(2)subprocess.Popen()函数的使用
subprocess.Popen()提供了更多的灵活性,可以启动一个新的子进程并允许与其进行交互,而不仅仅是等待它完成。
前面介绍的subprocess.run()其实调用的是subprocess.Popen()对象,再进行封装,所以subprocess.run()函数的底层其实就是subprocess.Popen()函数。subprocess.run()函数要等待子进程实行结束后才返回,或者可能导致超时,而Popen创建子进程后并与其进行交互,也可以等待进程完成、检查其返回码或终止它。例如:
动手练一练:
import subprocess
# 执行命令
proc = subprocess.Popen(["python", "-c", "print('Hello World')"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
# communicate()函数读取标准输出和错误后,返回一个元祖,并分别赋值给a和b
a, b = proc.communicate()
print(a) # 标准输出 Hello World
print(b) # 由于没有捕获标准错误,所以输出 None
执行以上代码,输出结果为:
Hello World
None
在上面的示例中,首先使用subprocess.Popen()来启动进程,指定stdout=subprocess.PIPE捕获标准输出,指定stderr=subprocess.PIPE捕获标准错误。然后,使用process.communicate()方法来等待进程完成并获取其输出。process.communicate()会返回一个元祖,包含标准输出和错误两个元素,我们把该元祖分别赋值给a和b,最后打印出标准输出和错误。
我们同样可以使用subprocess.Popen()方法运行Python脚本。首先,我们打开目录“Pyhint\condition”(Pyhint编辑器默认的工作路径),在该目录下创建一个简单的Python脚本文件hello.py,打开hello.py文件输入print("Hello World!")并保存。最后打开Pyhint编辑器,在代码框中输入以下内容,并点击运行:
动手练一练:
import subprocess
# 执行命令
proc = subprocess.Popen(["python", "hello.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
# communicate()函数读取标准输出和错误后,返回一个元祖,并分别赋值给a和b
a, b = proc.communicate()
print(a) # 标准输出 Hello World!
print(b) # 由于没有捕获标准错误,所以输出 None
执行以上代码,输出结果为:
Hello World!
None
3、subprocess模块的应用场景
subprocess模块在执行系统命令、调用其他程序、进行进程间通信等场景提供了强大而灵活的功能。它可以帮助你与外部其他软件程序进行交互,获取和处理执行结果。subprocess模块常用的场景包括:
-
测试和调试运用程序:在测试和调试阶段,可以使用subprocess模块来模拟外部的Python环境,以便更好地调试和控制程序的执行。
-
运行可执行文件或程序:当需要从Python脚本中调用其他的可执行文件或程序时,可以使用subprocess模块来启动和监控这些进程,如运行Windows上的.exe文件,并控制其行为。例如,使用Subprocess模块运行执行特定任务的可执行文件,然后在自己的代码中使用该可执行文件的输出。这对于图像处理、数据分析和机器学习任务非常有用。
-
自动化任务:使用subprocess模块可以轻松地自动化常见的系统任务,例如文件管理、系统监控、网络管理等等。
-
批量处理数据:在一些数据处理任务中,需要调用外部工具或脚本来处理数据,subprocess模块可以批量执行这些处理任务。
-
执行并行任务:在一些场景下,需要同时执行多个任务,可以通过subprocess模块来实现并行执行。