如何高效调用`subprocess`(并避免循环调用)

问题描述 投票:0回答:1

我有一个 Python 脚本,其中包含一个循环访问项目列表的 for 循环。我需要对每个项目的属性执行计算,但执行此计算的代码是用 Java 编写的(其中 java

main()
方法接受两个参数:下例中的
arg1
arg2
)。到目前为止,一切顺利——我可以使用
subprocess
来调用 Java。

这就是我目前的做法(简化):

from subprocess import Popen, PIPE

cp = ... # my classpath string
java_file = ... # the file with the java code
arg1 = ... # an argument string (always the same value) 
items = [...] # my list of items
for item in items:
    args2 = ... # calculated from item inside the python script 
    cmd = ['java', '-cp', cp, java_file, arg1, arg2]
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
    output, errors = process.communicate()
    outp_str = output.decode('utf-8') # the result I need

它可以工作,但是因为我的列表可以包含数千个元素,所以我会多次调用

subprocess
- 这似乎非常低效。

有没有一种方法可以让我在循环之前仅调用

subprocess
一次,然后在循环内为活动子进程提供必要的命令?或者这在速度/效率方面没有意义吗?

我发现了this问题,这似乎是相关的——但我无法设法将其转化为我的场景。我也没有在子流程的文档中找到我的解决方案。我想它会是这样的:

cp = ... # my classpath string
java_file = ... # the file with the java code
arg1 = ... # an argument string (always the same value)
cmd = [...] # <-- ???
process = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) 
items = [...] # my list of items
for item in items:
    args2 = ... # calculated from item inside the python script
    process.stdin.write(bytes(..., 'utf-8')) # <-- ???
    process.stdin.flush()
    result = process.stdout.readline() # the result I need 

...我无法弄清楚这两个命令应该是什么(在带有问号的行中)。

我想要的可能吗?非常感谢任何帮助!

python for-loop subprocess
1个回答
0
投票

能否让你的Python代码更加“高效”取决于Java应用程序的实现方式。如果 Java 应用程序只能通过命令行参数接收输入,那么您无能为力。您必须为每对参数启动一个新的子流程。但是,如果您的 Java 应用程序被实现为从其标准输入读取,那么您可以从 Python 端写入

stdin
。这取决于 Java 应用程序决定的协议。

您还会问,如果可以写入标准输入,Java 命令会是什么样子。启动 Java 应用程序的命令是相同的。可能不同的是您需要传递给 Java 应用程序的命令行参数(如果有)。这又取决于 Java 应用程序的实现方式。

注意,重用相同的子流程比为每对参数启动一个新的子流程会更便宜。尤其是 Java 的启动时间相对较长。但您是否可以执行此操作取决于 Java 应用程序。

这是一个示例 Java 应用程序,可以在“处理”两个命令行参数和“处理”来自标准输入的未知数量的参数对之间切换。

package sample; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Scanner; public class Main { public static void main(String[] args) throws IOException { if (args.length == 1 && args[0].equals("--use-stdin")) { processArgsFromStandardInput(); } else if (args.length == 2) { processArgs(args[0], args[1]); } else { System.err.println("Illegal command line. Must be --use-stdin or 2 arguments."); System.exit(1); } } static void processArgsFromStandardInput() throws IOException { Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8); scanner.useDelimiter(","); while (scanner.hasNext()) { String arg1 = scanner.next(); String arg2 = scanner.next(); processArgs(arg1, arg2); } } static void processArgs(String arg1, String arg2) { System.out.printf("Processing args: %s, %s%n", arg1, arg2); } }

我选择使用 
Scanner

,但您可以使用任何您想要的(例如,

BufferedReader
DataInputStream
等)。重要的是数据源是
System.in
(标准输入)。我还选择使用
","
作为参数之间的分隔符。同样,这是一个任意的选择,你可以使用任何你想要的。尽管这意味着参数本身不能包含逗号(我不提供“转义”逗号的方法)。注意使用UTF-8编码和逗号作为分隔符就是我之前提到的“协议”。
这是一个示例 Python 脚本,它调用 Java 应用程序(编译并打包到 JAR 文件中)两次,每个“模式”调用一次:

import subprocess import sys from subprocess import Popen, PIPE from time import time def measure_time(func): def wrapper(*args): start = time() func(*args) end = time() print(f'Function took {end - start:.2f} seconds.') return wrapper def pairwise(iterable): a = iter(iterable) return zip(a, a) @measure_time def invoke_args(jarfile, args): for arg1, arg2 in pairwise(args): subprocess.run(['java', '-jar', jarfile, arg1, arg2]) @measure_time def invoke_stdin(jarfile, args): with Popen(['java', '-jar', jarfile, '--use-stdin'], stdin=PIPE) as proc: for arg1, arg2 in pairwise(args): proc.stdin.write(f'{arg1},{arg2},'.encode()) if __name__ == '__main__': jarfile = sys.argv[1] args = [f'arg{i}' for i in range(1, 41)] print('========== COMMAND LINE ARGS ==========') invoke_args(jarfile, args) print() print('========= STANDARD INPUT ==========') invoke_stdin(jarfile, args) print()

如果您调用上述 Python 脚本(传递适当的 JAR 文件路径并假设您的路径上有 Java),那么您应该看到类似以下内容的输出:

========== COMMAND LINE ARGS ========== Processing args: arg1, arg2 Processing args: arg3, arg4 Processing args: arg5, arg6 Processing args: arg7, arg8 Processing args: arg9, arg10 Processing args: arg11, arg12 Processing args: arg13, arg14 Processing args: arg15, arg16 Processing args: arg17, arg18 Processing args: arg19, arg20 Processing args: arg21, arg22 Processing args: arg23, arg24 Processing args: arg25, arg26 Processing args: arg27, arg28 Processing args: arg29, arg30 Processing args: arg31, arg32 Processing args: arg33, arg34 Processing args: arg35, arg36 Processing args: arg37, arg38 Processing args: arg39, arg40 Function took 2.46 seconds. ========= STANDARD INPUT ========== Processing args: arg1, arg2 Processing args: arg3, arg4 Processing args: arg5, arg6 Processing args: arg7, arg8 Processing args: arg9, arg10 Processing args: arg11, arg12 Processing args: arg13, arg14 Processing args: arg15, arg16 Processing args: arg17, arg18 Processing args: arg19, arg20 Processing args: arg21, arg22 Processing args: arg23, arg24 Processing args: arg25, arg26 Processing args: arg27, arg28 Processing args: arg29, arg30 Processing args: arg31, arg32 Processing args: arg33, arg34 Processing args: arg35, arg36 Processing args: arg37, arg38 Processing args: arg39, arg40 Function took 0.16 seconds.

© www.soinside.com 2019 - 2024. All rights reserved.