【问题标题】:Calling Python 2 script from Python 3从 Python 3 调用 Python 2 脚本
【发布时间】:2015-01-09 15:25:52
【问题描述】:

我有两个脚本,主要是用 Python 3 编写的,第二个是用 Python 2 编写的(它也使用 Python 2 库)。

我想从 Python 3 脚本调用 Python 2 脚本中的一种方法,但我不知道如何跨过这座桥。

【问题讨论】:

  • 该特定方法是否依赖于 Python 2 特定的功能?可以转换成 Python 3 吗?
  • 如果 3x 不支持它,那么你根本无法运行它。
  • 另一种方法可能是通过系统调用调用 python 2 脚本并将结果存储在一个文件中......但前提是所有其他方法都失败了。
  • @GaryYe 如果您需要的输出可以转换为文本文件或类似文件,您可以创建一个 python2 脚本输出您需要的内容(到文件或控制台)。然后,您可以通过 python3 中的子进程运行脚本(假设您同时安装了 Python 2.x 和 Python 3.x)并通过子进程或打开由 Python 2 脚本创建的文件获取结果。
  • 是的,只需像桥接两种完全不同的语言一样桥接它......只需要想出某种接口,该接口适用于您尝试的任何数据路过

标签: python


【解决方案1】:

使用execnet 可以非常优雅地相互调用不同的python 版本。以下函数具有魅力:

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()

示例:用 Python 2.7 编写的 my_module.py

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

然后下面的函数调用

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)

结果

Hello Mr Bear!
Hello Mrs Wolf!

发生的事情是“网关”被实例化等待 对于带有channel.receive() 的参数列表。一旦它进来,它就被翻译并传递给my_functionmy_function 返回它生成的字符串,channel.send(...) 将字符串发回。在网关的另一端channel.receive() 捕获该结果并将其返回给调用者。调用者最终打印出 python 3 模块中my_function 生成的字符串。

【讨论】:

  • 可以发送和接收文本以外的对象吗?
  • 是的,你可以!这就是'execnet'的好处。它以兼容的方式序列化对象。
  • @Frank-ReneSchäfer 我尝试运行您的函数来调用返回浮点数和字符串元组的 python2 函数。出现错误提示 DumpError: can't serialize <type 'numpy.float64'>。知道如何解决这个问题吗?
  • 谁能评论这种方法相对于纯 Python 2 的性能?我认为启动另一个 Python 解释器以及序列化和反序列化肯定会受到一些惩罚。
  • On the page: "execnet 目前处于仅维护模式,主要是因为它仍然是pytest-xdist 插件的后端。不要在新项目中使用。"唔。现在呢?
【解决方案2】:

您可以使用子进程(python 模块)从 bash 运行 python2,执行以下操作:

来自 python 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

输出存储python 2返回的任何内容

【讨论】:

  • 如果你想等待子进程完成后再执行下一个 Python 行,考虑使用subprocess.callstackoverflow.com/a/89243/3822261
  • 我不明白这是怎么回答的。这使用 python3 解释器。
  • 对我来说它抛出OSError: [Errno 2] No such file or directory(我正在尝试从 Py 2 启动 Py 3;示例目标脚本只有 1 个打印语句)。我也很困惑这是否像 frakman1 所说的那样有效,或者不像 @MatthewLueder 暗示的那样有效?
  • 什么不起作用?您是否使用正确的路径来调用命令?
  • 在为我的 python2 脚本使用绝对路径时出现“Exec 格式错误”,但后来我在其顶部添加了 #!/usr/bin/env python2.7 行,它可以正常工作.
【解决方案3】:

也许为时已晚,但调用 python2.7 脚本还有一个更简单的选择:

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

【讨论】:

  • 甚至稍后......“python2.7”我假设您的意思是 Python 2.7 可执行文件,例如:“C:\Program Files\Python27\python.exe”。注意嵌套引号,以便在 Program Files 目录中留出空间(我仍然认为这是 Microsoft 有史以来最糟糕的 brailf**ts 之一)。
【解决方案4】:

如果我直接从 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()

【讨论】:

    【解决方案5】:

    我正在使用 python 3 运行我的 python 代码,但我需要一个使用 python 2.7 编写的工具 (ocropus)。我花了很长时间尝试使用 subprocess 的所有这些选项,并且一直出现错误,并且脚本无法完成。从命令行,它运行得很好。所以我终于尝试了一些简单的方法,但我在网上搜索时没有找到。我将 ocropus 命令放在 bash 脚本中:

    #!/bin/bash
    
    /usr/local/bin/ocropus-gpageseg $1
    

    我用子进程调用 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)
    

    这确实为 ocropus 脚本提供了它自己的小世界,它似乎需要它。我发布此内容是希望它可以节省其他人一些时间。

    【讨论】:

    • Shell 命令可能会带来安全风险。不推荐。
    【解决方案6】:

    我最终在 python3 脚本中创建了一个新函数,它包装了 python2.7 代码。它正确格式化由 python2.7 代码创建的错误消息,并扩展 mikelsr 的答案,并按照subprocess docs 的建议使用run()

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

    def foo27(input):
        return input * 2
    

    在你的 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
    

    这在传入简单的python对象(如dicts)作为参数时有效,但不适用于由类创建的对象,例如。 numpy 数组。这些必须在屏障的另一侧进行序列化和重新实例化。

    【讨论】:

      【解决方案7】:

      我建议将 Python2 文件转换为 Python3:

      https://pythonconverter.com/

      【讨论】:

      • 这有时不起作用。一些 Python2 模块在 Python 3 上不起作用。问题中的脚本使用 Python2 库,所以这不起作用
      【解决方案8】:

      注意:这是在 liclipse IDE 中运行我的 python 2.x s/w 时发生的。 当我从命令行上的 bash 脚本运行它时,它没有问题。 这是我在混合 python 2.x 和 3.x 脚本时遇到的问题和解决方案。

      我正在运行一个 python 2.6 进程并且需要调用/执行一个 python 3.6 脚本。 环境变量 PYTHONPATH 设置为指向 2.6 python s/w,所以它被以下内容阻塞:

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

      这导致 3.6 python 脚本失败。 因此,我没有直接调用 3.6 程序,而是创建了一个 bash 脚本,它对 PYTHONPATH 环境变量进行了核对。

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

      【讨论】:

        猜你喜欢
        • 2021-09-08
        • 2023-03-27
        • 1970-01-01
        • 2014-06-25
        • 2016-04-29
        • 2018-03-25
        • 2021-10-19
        • 2016-03-02
        • 2017-09-25
        相关资源
        最近更新 更多