从Go中调用Python函数并获取函数的返回值

huangapple go评论106阅读模式
英文:

Calling Python function from Go and getting the function return value

问题

我正在编写一个Go程序。从这个Go程序中,我想调用另一个文件中定义的Python函数,并接收函数的返回值,以便在我的Go程序中进行后续处理。然而,我在我的Go程序中无法获取任何返回的数据。以下是我认为应该起作用但实际上并不起作用的最小示例:

gofile.go

package main

import "os/exec"
import "fmt"

func main() {
     fmt.Println("here we go...")
     program := "python"
     arg0 := "-c"
     arg1 := fmt.Sprintf("'import pythonfile; print pythonfile.cat_strings(\"%s\", \"%s\")'", "foo", "bar")
     cmd := exec.Command(program, arg0, arg1)
     fmt.Println("command args:", cmd.Args)
     out, err := cmd.CombinedOutput()
     if err != nil {
         fmt.Println("Concatenation failed with error:", err.Error())
	 return
     }
     fmt.Println("concatentation length: ", len(out))
     fmt.Println("concatenation: ", string(out))
     fmt.Println("...done")
}

pythonfile.py

def cat_strings(a, b):
    return a + b

如果我调用go run gofile,我会得到以下输出:

here we go...
command args: [python -c 'import pythonfile; print pythonfile.cat_strings("foo", "bar")']
concatentation length:  0
concatenation:  
...done

一些注意事项:

  • 我在Python调用中使用了-c标志,这样我就可以直接调用函数cat_strings。假设cat_strings是一个包含实用函数的Python文件的一部分,这些函数被其他Python程序使用,这就是为什么我没有任何if __name__ == __main__的原因。

  • 我不想修改Python文件以打印a + b(而不是return a + b);请参考前面关于该函数是一组实用函数的一部分,应该可以被其他Python代码调用的说明。

  • cat_strings函数是虚构的,仅用于演示目的;真正的函数是我不想简单地在Go中重新实现的。我真正感兴趣的是如何从Go中调用Python函数并获取返回值。

英文:

I am writing a Go program. From this Go program, I would like to call a Python function defined in another file and receive the function's return value so I can use it in subsequent processing in my Go program. I am having trouble getting any returned data back in my Go program though. Below is a minimum example of what I thought would work, but apparently doesn't:

gofile.go

package main

import "os/exec"
import "fmt"

func main() {
     fmt.Println("here we go...")
     program := "python"
     arg0 := "-c"
     arg1 := fmt.Sprintf("'import pythonfile; print pythonfile.cat_strings(\"%s\", \"%s\")'", "foo", "bar")
     cmd := exec.Command(program, arg0, arg1)
     fmt.Println("command args:", cmd.Args)
     out, err := cmd.CombinedOutput()
     if err != nil {
         fmt.Println("Concatenation failed with error:", err.Error())
	 return
     }
     fmt.Println("concatentation length: ", len(out))
     fmt.Println("concatenation: ", string(out))
     fmt.Println("...done")
}

pythonfile.py

def cat_strings(a, b):
    return a + b

If I call go run gofile I get the following output:

here we go...
command args: [python -c 'import pythonfile; print pythonfile.cat_strings("foo", "bar")']
concatentation length:  0
concatenation:  
...done

A few notes:

  • I'm using the -c flag in the Python invocation so I can call the function cat_strings directly. Assume cat_strings is part of a Python file full of utility functions that are used by other Python programs, hence why I don't have any if __name__ == __main__ business.
  • I don't want to modify the Python file to print a + b (instead of return a + b); see the prior point about the function being part of a set of utility functions that ought to be callable by other Python code.
  • The cat_strings function is fictional and for demonstration purposes; the real function is something I don't want to simply reimplement in Go. I really am interested in how I can call a Python function from Go and get the return value.

答案1

得分: 18

我设法通过简单地删除命令本身周围的引号来获得一些可工作的代码:

package main

import "fmt"
import "os/exec"

func main() {
    cmd := exec.Command("python", "-c", "import pythonfile; print pythonfile.cat_strings('foo', 'bar')")
    fmt.Println(cmd.Args)
    out, err := cmd.CombinedOutput()
    if err != nil { fmt.Println(err); }
    fmt.Println(string(out))
}

确实,在source中,你有这个函数(至少适用于Windows,我不知道其他操作系统是否适用):

// EscapeArg rewrites command line argument s as prescribed
// in http://msdn.microsoft.com/en-us/library/ms880421.
// This function returns "" (2 double quotes) if s is empty.
// Alternatively, these transformations are done:
// - every back slash (\) is doubled, but only if immediately
//   followed by double quote (");
// - every double quote (") is escaped by back slash (\);
// - finally, s is wrapped with double quotes (arg -> "arg"),
//   but only if there is space or tab inside s.
func EscapeArg(s string) string { ...

因此,你的代码最终传递了以下命令行调用:

$ python -c "'import pythonfile; print pythonfile.cat_strings(\\\"foo\\\", \\\"bar\\\")'"

如果测试,它将计算为一个字符串并返回空,因此输出长度为0。

英文:

I managed to have some working code for this by simply removing the quote around the command itself:

package main

import "fmt"
import "os/exec"

func main() {
    cmd := exec.Command("python",  "-c", "import pythonfile; print pythonfile.cat_strings('foo', 'bar')")
    fmt.Println(cmd.Args)
    out, err := cmd.CombinedOutput()
    if err != nil { fmt.Println(err); }
    fmt.Println(string(out))
}

And sure enough, in the source, you have this function (for Windows, at least, I don't know if that works for other OSes):

// EscapeArg rewrites command line argument s as prescribed
// in http://msdn.microsoft.com/en-us/library/ms880421.
// This function returns "" (2 double quotes) if s is empty.
// Alternatively, these transformations are done:
// - every back slash (\) is doubled, but only if immediately
//   followed by double quote (");
// - every double quote (") is escaped by back slash (\);
// - finally, s is wrapped with double quotes (arg -> "arg"),
//   but only if there is space or tab inside s.
func EscapeArg(s string) string { ...

So your code is ending up passing the following command line call:

$ python -c "'import pythonfile; print pythonfile.cat_strings(\\"foo\\", \\"bar\\")'"

Which, if tested, evaluates to a string and returns nothing, hence the 0-length output.

huangapple
  • 本文由 发表于 2013年10月16日 15:56:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/19397986.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定