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

dop*_*man 27 julia

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

# Hello.jl
module Hello
    function foo
        return 1
    end
end
Run Code Online (Sandbox Code Playgroud)

# Main.jl
using Hello
foo()
Run Code Online (Sandbox Code Playgroud)

当我运行Main模块时:

$ julia ./Main.jl
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

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
Run Code Online (Sandbox Code Playgroud)

kil*_*ics 35

自Julia v0.7和v1.0发布以来,这个问题有一个新的答案,略有不同.我只是必须这样做所以我想我会在这里发布我的发现.

如在其他解决方案中已经解释的那样,有必要包括定义模块的相关脚本.但是,由于自定义模块不是包,因此无法将其作为包装加载,使其与旧版Julia版本中的相同usingimport命令一样.

所以Main.jl脚本将使用这样的相对导入编写:

include("./Hello.jl")
using .Hello
foo()
Run Code Online (Sandbox Code Playgroud)

我在Stefan Karpinski关于类似问题的话语评论中简单地解释了这一点.正如他所描述的那样,处理子模块时情况也会更加精细.模块路径文档部分也是一个很好的参考.

  • 第二个链接已失效。截至撰写本文时的信息可在此处获取:https://docs.julialang.org/en/v1/manual/modules/#Modules-and-files-1 (3认同)
  • 我正在使用 Julia 1.2,看来没有必要再使用 `using .name` 语句了。执行 `include("./name")` 足以将文件及其函数导入到当前脚本中。 (2认同)
  • @theferrit32 `include` 和 `using` 本质上是不同的。与 C/C++ 非常相似,“include”将指定文件的源代码带入调用文件中,就像直接在调用文件中写入另一个文件一样。另一方面,“using”与文件无关(除非在“LOAD_PATH”中找到同名文件)。它用于将特定模块的符号导入到调用模块中。每个文件可以定义多个模块,每个模块都需要在同级模块上调用“using”或“import”才能访问它们的功能。 (2认同)

Rem*_*elt 16

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

当你运行时using MyModule,Julia只在一个名为你的目录列表中搜索它LOAD_PATH.如果您输入LOAD_PATHJulia REPL,您将获得以下内容:

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"
Run Code Online (Sandbox Code Playgroud)

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

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

julia> include("./src/Hello.jl")
Run Code Online (Sandbox Code Playgroud)

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

添加目录到 LOAD_PATH

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

要做到这一点的方法之一是以下添加到您的.basrc,.profile,.zshrc.

export JULIA_LOAD_PATH="/path/to/module/storage/folder"
Run Code Online (Sandbox Code Playgroud)

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

julia> LOAD_PATH
Run Code Online (Sandbox Code Playgroud)

它应该回来

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"
Run Code Online (Sandbox Code Playgroud)

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

有关更多信息,请查看Julia Docs 中的页面.


张实唯*_*张实唯 13

你应该include("./Hello.jl")在之前using Hello

  • 那么"使用"的意义是什么?我以为这个kw会包含给我的模块...... (5认同)
  • 在较新的 Julia 版本中,您只需要包含。不再需要“使用” (2认同)
  • @LucasCavalcantiRodrigues,不完全是。“包含”文件中包含的模块的副作用是文件内容是在全局范围内评估的。这并不总是(如果有的话)可取的。请参阅:https://docs.julialang.org/en/v1/manual/code-loading/ (2认同)

Gra*_*ith 11

虽然张实唯的答案是最方便的,但你不应该include在REPL之外使用.如果您正在编写程序文件,请务必将相应的目录添加到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
Run Code Online (Sandbox Code Playgroud)

a_function.jl

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

function_caller.jl

include("a_function.jl")
include("types.jl")
using TypeModule, AFunctionModule
my_a = A()
takes_a(my_a)
Run Code Online (Sandbox Code Playgroud)

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

编辑以响应xji:要分发模块,您可以使用Pkg(docs).我理解这个问题的前提是一个定制的个人模块.

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

  • 如果程序要分发并在其他用户的系统上运行,那实际上似乎不是可移植的,对吗?这样每个用户都必须修改他们的环境变量才能在他们的系统上运行每个新的 Julia 程序? (2认同)