我使用 numpy、pandas、scikit-learn 在 Python 中编写了一些代码。是否可以从 Julia 程序调用此 Python 代码?
[2025 年更新]
较新的软件包 PythonCall.jl 已成为 Julia 与 Python 接口的事实上的标准。我在我的书的第二版中广泛讨论了它的用法,但是您可以在上面的链接中找到它的良好文档,或者在这里获取书中使用的源代码(Julia/Python 接口位于第 1 章)。 7).
[/2025 年更新]
我认为你可以想到从 Julia 调用 Python 代码的三种不同方式,按照从最低级别到最高级别的顺序:
按照@madbird的建议使用Foreign Funcall接口。然而,您几乎肯定不想这样做,因为利用它的包PyCall.jl已经存在;
使用上述 PyCall.jl 包调用任何 python 代码和/或包装 python 库(好吧..“大多数”..“任何”是一个危险的词)。详情如下;
由于使用 PyCall 封装 Python 库非常简单,最重要的 Python 库已经封装在 Julia 中,例如 Pandas.jl 中的 Pandas、ScikitLearn.jl 中的 Scikit-learn 等。如果您需要它们,使用对应的Julia包即可。
注意:以下摘录自我的《Julia Quick Syntax Reference》一书(Apress,2019)
在 Julia 中调用 Python 代码的“标准”方法是使用 PyCall 包。 它的一些不错的功能是:(a)它可以自动下载并安装 Julia 私有的 Python 本地副本,以避免混淆我们“主”Python 安装的版本依赖性,并在 Linux、Windows 中提供一致的环境和 MacOS; (b) 它提供 Julia 和 Python 类型之间的自动转换; (c) 使用起来非常简单。
关于第一点,
PyCall
在Windows和MacOS中默认安装“私有”Python环境,而在Linux中它将使用系统默认的Python环境。 +
我们可以使用(在 Julia 提示符下)ENV["PYTHON"]="blank or /path/to/python"; using Pkg; Pkg.build("PyCall");
覆盖此类行为,其中,如果环境变量为空,PyCall
将安装“私有”版本的 Python。
考虑到大量的 Python 库,难怪
PyCall
是最常见的 Julia 包之一。
在 Julia 程序中嵌入 Python 代码与我们在 C++ 中看到的类似,只是我们(大多数情况下)不需要考虑转换数据。我们都使用
py"..."
定义和调用 Python 函数,并且在函数调用中我们可以直接使用我们的 Julia 数据:
using PyCall
py"""
def sumMyArgs (i, j):
return i+j
def getNElement (n):
a = [0,1,2,3,4,5,6,7,8,9]
return a[n]
"""
a = py"sumMyArgs"(3,4) # 7
b = py"sumMyArgs"([3,4],[5,6]) # [8,10]
typeof(b) # Array{Int64,1}
c = py"sumMyArgs"([3,4],5) # [8,9]
d = py"getNElement"(1) # 1
请注意,即使是像数组这样的复杂数据,我们也不需要进行转换,结果会转换回 Julia 类型。 对于数字、布尔值、字符串、IO 流、日期/周期和函数类型以及这些类型的元组、数组/列表和字典,类型转换是自动的。其他类型会转换为通用
PyObject
类型。
注意上一个示例的最后一行,
PyCall
不会尝试索引转换(Python 数组是从 0 开始的,而 Julia 数组是从 1 开始的):使用“1”作为参数调用 python getNElement()
函数将检索 在 Python 中 是数组的元素“1”。
使用 Python 库也很简单,如下面的示例所示,使用 ezodf 模块创建 OpenDocument 电子表格(ODS 文档的
ezodf
包装器 - 内部使用 PyCall - 已经存在,OdsIO ).
在尝试复制以下代码之前,请确保
ezodf
模块可用于您在 Julia 中使用的 Python 环境。如果这是一个独立的环境,只需按照Python的方式安装包(例如使用pip
)。如果您使用“私有”Conda 环境,则可以使用 Conda.jl 包并输入 using Conda; Conda.add_channel("conda-forge"); Conda.add("ezodf")
。
const ez = pyimport("ezodf") # Equiv. of Python `import ezodf as ez`
destDoc = ez.newdoc(doctype="ods", filename="anOdsSheet.ods")
sheet = ez.Sheet("Sheet1", size=(10, 10))
destDoc.sheets.append(sheet)
dcell1 = get(sheet,(2,3)) # Equiv. of Python `dcell1 = sheet[(2,3)]`. This is cell "D3" !
dcell1.set_value("Hello")
get(sheet,"A9").set_value(10.5) # Equiv. of Python `sheet['A9'].set_value(10.5)`
destDoc.backup = false
destDoc.save()
该模块在 Julia 中的使用遵循 Python API,几乎没有语法差异。 该模块已导入并分配给更短的别名
ez
。
然后我们可以使用常用的 Python 语法直接调用它的函数module.function()
。
doc
返回的 newdoc
对象是通用 PyObject
类型。然后我们可以分别使用 myPyObject.attribute
和 myPyObject.method()
访问其属性和方法。
在我们无法直接访问某些指示值的情况下,例如 sheet[(2,3)]
(其中索引是一个元组),我们可以调用 get(object,key)
函数。 +
最后,再次注意,索引转换不是自动实现:当请求get(sheet,(2,3))
时,这些被解释为基于Python的索引,并且返回电子表格的单元格D3
,而不是B2
。