我有一个 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代码更加“高效”取决于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.