简体   繁体   English

从 Python 3 调用 Python 2 脚本

[英]Calling Python 2 script from Python 3

I have two scripts, the main is in Python 3, and the second one is written in Python 2 (it also uses a Python 2 library).我有两个脚本,主要在Python 3,第二个写在Python 2(它还用了一个Python 2库)。

There is one method in the Python 2 script I want to call from the Python 3 script, but I don't know how to cross this bridge. Python 2 脚本中有一个方法我想从Python 3 脚本中调用,但我不知道如何过这个桥。

Calling different python versions from each other can be done very elegantly using execnet .使用execnet可以非常优雅地相互调用不同的 python 版本。 The following function does the charm:以下函数具有魅力:

import execnet

def call_python_version(Version, Module, Function, ArgumentList):
    gw      = execnet.makegateway("popen//python=python%s" % Version)
    channel = gw.remote_exec("""
        from %s import %s as the_function
        channel.send(the_function(*channel.receive()))
    """ % (Module, Function))
    channel.send(ArgumentList)
    return channel.receive()

Example: A my_module.py written in Python 2.7:示例:用 Python 2.7 编写的my_module.py

def my_function(X, Y): 
    return "Hello %s %s!" % (X, Y)

Then the following function calls然后下面的函数调用

result = call_python_version("2.7", "my_module", "my_function",  
                             ["Mr", "Bear"]) 
print(result) 
result = call_python_version("2.7", "my_module", "my_function",  
                             ["Mrs", "Wolf"]) 
print(result)

result in导致

Hello Mr Bear!
Hello Mrs Wolf!

What happened is that a 'gateway' was instantiated waiting for an argument list with channel.receive() .发生的事情是一个“网关”被实例化,等待带有channel.receive()的参数列表。 Once it came in, it as been translated and passed to my_function .一旦它进来,它就会被翻译并传递给my_function my_function returned the string it generated and channel.send(...) sent the string back. my_function返回它生成的字符串,然后channel.send(...)将字符串发回。 On other side of the gateway channel.receive() catches that result and returns it to the caller.在网关的另一端channel.receive()捕获该结果并将其返回给调用者。 The caller finally prints the string as produced by my_function in the python 3 module.调用者最终打印由my_function在 python 3 模块中生成的字符串。

You could run python2 from bash using subprocess (python module) doing the following:您可以使用子进程(python 模块)从 bash 运行 python2,执行以下操作:

From python 3 :蟒蛇3

#!/usr/bin/env python3
import subprocess

python3_command = "py2file.py arg1 arg2"  # launch your python2 script using bash

process = subprocess.Popen(python3_command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()  # receive output from the python2 script

Where output stores whatever python 2 returned输出存储python 2返回的任何内容

Maybe to late, but there is one more simple option for call python2.7 scripts:也许为时已晚,但还有一个更简单的调用 python2.7 脚本的选项:

script = ["python2.7", "script.py", "arg1"]    
process = subprocess.Popen(" ".join(script),
                                        shell=True,  
                                        env={"PYTHONPATH": "."})

I am running my python code with python 3, but I need a tool (ocropus) that is written with python 2.7.我正在使用 python 3 运行我的 python 代码,但我需要一个用 python 2.7 编写的工具(ocropus)。 I spent a long time trying all these options with subprocess, and kept having errors, and the script would not complete.我花了很长时间在子进程中尝试所有这些选项,并不断出错,脚本无法完成。 From the command line, it runs just fine.从命令行,它运行得很好。 So I finally tried something simple that worked, but that I had not found in my searches online.所以我终于尝试了一些简单有效的方法,但我在网上搜索中没有找到。 I put the ocropus command inside a bash script:我将 ocropus 命令放在一个 bash 脚本中:

#!/bin/bash

/usr/local/bin/ocropus-gpageseg $1

I call the bash script with subprocess.我用子进程调用 bash 脚本。

command = [ocropus_gpageseg_path,  current_path]
process = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
output, error = process.communicate()
print('output',output,'error',error)

This really gives the ocropus script its own little world, which it seems to need.这确实为 ocropus 脚本提供了它自己似乎需要的小世界。 I am posting this in the hope that it will save someone else some time.我发布此信息是希望它能为其他人节省一些时间。

It works for me if I call the python 2 executable directly from a python 3 environment.如果我直接从 python 3 环境调用 python 2 可执行文件,它对我有用。

python2_command = 'C:\Python27\python.exe python2_script.py arg1'
process = subprocess.Popen(python2_command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

python3_command = 'python python3_script.py arg1'
process = subprocess.Popen(python3_command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

I ended up creating a new function in the python3 script, which wraps the python2.7 code.我最终在 python3 脚本中创建了一个新函数,它包装了 python2.7 代码。 It correctly formats error messages created by the python2.7 code and is extending mikelsr's answer and using run() as recommended by subprocess docs .它正确格式化由 python2.7 代码创建的错误消息,并且正在扩展 mikelsr 的答案并按照 subprocess docs 的建议使用run()

in bar.py (python2.7 code):在 bar.py(python2.7 代码)中:

def foo27(input):
    return input * 2

in your python3 file:在你的 python3 文件中:

import ast
import subprocess

def foo3(parameter):
    try:
        return ast.literal_eval(subprocess.run(
            [
                "C:/path/to/python2.7/python.exe", "-c", # run python2.7 in command mode
                "from bar import foo27;"+
                "print(foo27({}))".format(parameter) # print the output 
            ],
            capture_output=True,
            check=True
        ).stdout.decode("utf-8")) # evaluate the printed output
    except subprocess.CalledProcessError as e:
        print(e.stdout)
        raise Exception("foo27 errored with message below:\n\n{}"
                                .format(e.stderr.decode("utf-8")))
print(foo3(21))
# 42

This works when passing in simple python objects, like dicts, as the parameter but does not work for objects created by classes, eg.这在传入简单的 Python 对象(如 dicts)作为参数时有效,但不适用于由类创建的对象,例如。 numpy arrays. numpy 数组。 These have to be serialized and re-instantiated on the other side of the barrier.这些必须在屏障的另一侧进行序列化和重新实例化。

Note: This was happening when running my python 2.xs/w in the liclipse IDE.注意:这是在 liclipse IDE 中运行我的 python 2.xs/w 时发生的。 When I ran it from a bash script on the command line it didn't have the problem.当我在命令行上从 bash 脚本运行它时,它没有问题。 Here is a problem & solution I had when mixing python 2.x & 3.x scripts.这是我在混合 python 2.x 和 3.x 脚本时遇到的一个问题和解决方案。

I am running a python 2.6 process & needed to call/execute a python 3.6 script.我正在运行 python 2.6 进程 & 需要调用/执行 python 3.6 脚本。 The environment variable PYTHONPATH was set to point to 2.6 python s/w, so it was choking on the followng:环境变量 PYTHONPATH 被设置为指向 2.6 python s/w,因此它在以下内容中窒息:

File "/usr/lib64/python2.6/encodings/__init__.py", line 123
raise CodecRegistryError,\

This caused the 3.6 python script to fail.这导致 3.6 python 脚本失败。 So instead of calling the 3.6 program directly I created a bash script which nuked the PYTHONPATH environment variable.因此,我没有直接调用 3.6 程序,而是创建了一个 bash 脚本,它破坏了 PYTHONPATH 环境变量。

#!/bin/bash
export PYTHONPATH=
## Now call the 3.6 python scrtipt
./36psrc/rpiapi/RPiAPI.py $1

Instead of calling them in python 3, you could run them in conda env batch by creating a batch file as below:与其在 python 3 中调用它们,不如通过如下创建批处理文件在 conda env batch 中运行它们:

call C:\ProgramData\AnacondaNew\Scripts\activate.bat调用 C:\ProgramData\AnacondaNew\Scripts\activate.bat

C:\Python27\python.exe "script27.py" C:\ProgramData\AnacondaNew\python.exe "script3.py" C:\Python27\python.exe "script27.py" C:\ProgramData\AnacondaNew\python.exe "script3.py"

call conda deactivate调用 conda 停用

pause暂停

I recommend to convert the Python2 files to Python3:我建议将 Python2 文件转换为 Python3:

https://pythonconverter.com/ https://pythonconverter.com/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM