如何在julia中导入自定义模块

时间:2016-05-13 01:51:58

标签: julia

我有一个我在这里写的模块:

# Hello.jl
module Hello
    function foo
        return 1
    end
end

# Main.jl
using Hello
foo()

当我运行Main模块时:

$ julia ./Main.jl

我收到此错误:

ERROR: LoadError: ArgumentError: Hello not found in path
 in require at ./loading.jl:249
 in include at ./boot.jl:261
 in include_from_node1 at ./loading.jl:320
 in process_options at ./client.jl:280
 in _start at ./client.jl:378
while loading /Main.jl, in expression starting on line 1

7 个答案:

答案 0 :(得分:26)

自Julia v0.7和v1.0发行以来,此问题有了一个新的答案,该答案略有不同。我只需要这样做,就可以将发现的信息发布在这里。

正如其他解决方案中已经解释的那样,有必要包括定义模块的相关脚本。但是,由于自定义模块不是软件包,因此无法像以前的Julia版本一样使用相同的usingimport命令将其作为软件包加载。

因此,Main.jl脚本将使用这样的相对导入来编写:

include("./Hello.jl")
using .Hello
foo()

我发现在Stefan Karpinski's discourse comment中对此问题做了简单解释。正如他所描述的,在处理子模块时,情况也会变得更加复杂。 documentation section on module paths也是很好的参考。

答案 1 :(得分:15)

已经有一些简短的答案,但我希望尽可能提供更完整的答案。

当您运行using MyModule时,Julia只会在名为LOAD_PATH的目录列表中搜索它。如果您在Julia REPL中键入LOAD_PATH,您将获得以下内容:

2-element Array{ByteString,1}:
 "/Applications/Julia-0.4.5.app/Contents/Resources/julia/local/share/julia/site/v0.4"
 "/Applications/Julia-0.4.5.app/Contents/Resources/julia/share/julia/site/v0.4"

这些是Julia将在您键入using Hello时搜索要包含的模块的目录。在您提供的示例中,由于Hello中没有LOAD_PATH,因此Julia无法找到它。

如果要包含本地模块,可以指定其相对于当前工作目录的位置。

julia> include("./src/Hello.jl")

一旦包含文件,您就可以正常运行using Hello以获得所有相同的行为。对于一个关闭脚本,这可能是最好的解决方案。但是,如果您发现自己经常需要include()某组目录,则可以将其永久添加到LOAD_PATH

将目录添加到LOAD_PATH

如果您希望定期使用存储在Julia LOAD_PATH之外的特定模块,则手动将目录添加到LOAD_PATH可能会很麻烦。在这种情况下,您可以将其他目录附加到LOAD_PATH环境变量。无论何时发出importusing命令,Julia都会自动搜索这些目录。

执行此操作的一种方法是将以下内容添加到.basrc.profile.zshrc

export JULIA_LOAD_PATH="/path/to/module/storage/folder"

这会将该目录附加到Julia将搜索的标准目录中。如果你然后运行

julia> LOAD_PATH

它应该返回

3-element Array{ByteString,1}:
 "/path/to/module/storage/folder"
 "/Applications/Julia-0.4.5.app/Contents/Resources/julia/local/share/julia/site/v0.4"
 "/Applications/Julia-0.4.5.app/Contents/Resources/julia/share/julia/site/v0.4"

您现在可以自由运行using Hello,Julia会自动找到该模块(只要它存储在/path/to/module/storage/folder下面。

有关详细信息,请查看Julia Docs中的this页面。

答案 2 :(得分:11)

您应include("./Hello.jl")之前using Hello

答案 3 :(得分:9)

虽然张实唯的答案是最方便的,你不应该在REPL之外使用include 。如果您正在编写程序文件,请务必将相应的目录添加到LOAD_PATH。雷米给出了如何做的非常好的解释,但它也值得解释为什么你应该首先这样做。 (另外来自文档:push!(LOAD_PATH, "/Path/To/My/Module/"),但请注意您的模块和文件必须具有相同的名称)

问题在于,include的所有内容都会定义在您调用include 的位置,即使它也在其他地方定义。由于模块的目标是重复使用,您最终可能会在多个文件中使用MyModule。如果你在每个文件中调用include,那么每个文件都有自己的MyModule定义,即使它们是相同的,也会有不同的定义。这意味着MyModule中定义的任何数据(例如数据类型)都不会相同。

要了解这是一个巨大的问题,请考虑以下三个文件:

types.jl

module TypeModule
struct A end
export A
end

a_function.jl

include("types.jl")
module AFunctionModule
using TypeModule
function takes_a(a::A)
    println("Took A!")
end
export takes_a
end

function_caller.jl

include("a_function.jl")
include("types.jl")
using TypeModule, AFunctionModule
my_a = A()
takes_a(my_a)

如果您运行julia function_caller.jl,您将获得MethodError: no method matching takes_a(::TypeModule.A)。这是因为function_caller.jl中使用的类型A与a_function.jl中使用的类型不同。在这个简单的例子中,你实际上可以修复"通过颠倒function_caller.jl中包含的顺序(或者只是完全从function_caller.jl中删除include("types.jl")来解决问题!这不好!)。但是如果你想要另一个文件b_function.jl也使用TypeModule中定义的类型呢?你必须做一些非常hacky的事情。或者你可以修改你的LOAD_PATH,这样模块只定义一次。

编辑以响应xji:要分发模块,您可以使用Pkgdocs)。我理解这个问题的前提是一个自定义的个人模块。

顺便提一下,如果您真的不想修改加载路径(即使它只在单个脚本的范围内......),您可以将模块符号链接到包目录中(例如~/.julia/v0.6/MyModule/MyModule.jl)然后Pkg.add(MyModule)然后正常导入。我发现这有点麻烦了。

答案 4 :(得分:1)

除非您显式加载文件(include("./Hello.jl")),否则Julia会在LOAD_PATH变量中定义的目录中查找模块文件。

请参阅this page

答案 5 :(得分:0)

如果要在使用“using”导入模块时访问函数foo,则需要在模块的标题中添加“export foo”。

答案 6 :(得分:0)

我有Julia Version 1.4.2 (2020-05-23)。只是这个using .Hello对我有用。 但是,我不得不在using .Hello之前编译Hello模块。对于Hello的定义和使用脚本都在同一文件中,这是很有意义的。

相反,我们可以在一个文件中定义Hello,然后通过include("./Hello.jl");using .Hello在另一个文件中使用它