Как импортировать пользовательский модуль в 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
6 ответов
уже было несколько коротких ответов, но я хотел бы дать более полный ответ, если это возможно.
при выполнении using MyModule
, Юлия ищет его только в списке каталогов, известных как ваш LOAD_PATH
. Если ввести LOAD_PATH
в Julia 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"
это каталоги, которые Джулия будет искать модули для включения при вводе using Hello
. В Примере, который вы предоставили, так как Hello
не был в LOAD_PATH
, Джулия не смогла найти его.
если вы хотите включить локального модуля, вы можете указать его местоположение относительно текущего рабочего каталога.
julia> include("./src/Hello.jl")
после того как файл был включен, вы можете затем запустить using Hello
как обычно, чтобы сделать все то же поведение. Для one off скриптов это, вероятно, лучшее решение. Однако, если вы обнаружите, что регулярно приходится include()
определенный набор каталогов, вы можете постоянно добавлять их в свой LOAD_PATH
.
добавление каталогов в LOAD_PATH
вручную добавление каталогов в ваш LOAD_PATH
может быть боль, если вы хотите регулярно использовать определенные модули, которые хранятся за пределами Юлия LOAD_PATH
. В этом случае, вы можете добавить дополнительные каталоги LOAD_PATH
переменные среды. Джулия будет автоматически искать через эти каталоги всякий раз, когда вы выдаете import
или .
один из способов сделать это чтобы добавить следующий код .basrc
, .profile
, .zshrc
.
export JULIA_LOAD_PATH="/path/to/module/storage/folder"
это добавит этот каталог в стандартные каталоги, которые Джулия будет искать. Если вы запустите
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
и Джулия автоматически найдет модуль (пока он хранится под /path/to/module/storage/folder
.
для получения дополнительной информации, взгляните на этой страница из Юлии Доктора.
хотя ответ 张实唯 является наиболее удобным,вы не должны использовать include
вне REPL. Если вы пишете файл программы, не забудьте добавить соответствующий каталог в LOAD_PATH. Реми дает очень хорошее объяснение, как это сделать, но стоит также объяснить, почему вы должны это сделать в первую очередь. (Дополнительно из документов:push!(LOAD_PATH, "/Path/To/My/Module/")
но обратите внимание, что ваш модуль и ваш файл должны иметь одно и то же имя)
проблема в том, что все, что угодно!--3--> будет определено прямо там, где вы вызываете include
даже если он и определен в другом месте. Поскольку целью модулей является повторное использование, вы, вероятно, в конечном итоге используете MyModule
в более чем один файл. Если вы позвоните include
в каждом файле, то каждый будет иметь свое собственное определение MyModule, и даже если они идентичны, это будут разные определения. Это означает любые данные, определенные в MyModule
(например, типы данных) не будут одинаковыми.
посмотреть почему это огромная проблема, рассмотрим эти три файла:
типы.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)
. Это потому, что тип A
используется в function_caller.jl отличается от того, который используется в a_function.как JL. В этом простом случае вы можете фактически "исправить" проблему, изменив порядок включения в function_caller.дл (или просто путем удаления include("types.jl")
полностью из function_caller.зл'! Это нехорошо!). Но что, если вам нужен другой файл b_function.jl, который также использовал тип, определенный в TypeModule
? Тебе придется сделать что-то очень банальное. Или вы можете просто изменить свой LOAD_PATH, чтобы модуль был определен только один раз.
EDIT в ответ на xji: для распространения модуля вы бы использовали Pkg
(docs). Я понял, что предпосылка этого вопроса является пользовательским, личным модулем.
кста, если вы действительно не нравится идея изменения пути загрузки (даже если это только в рамках одного сценария...) вы можете символически связать свой модуль с каталогом пакетов (например,~/.julia/v0.6/MyModule/MyModule.jl
) и нажмите Pkg.add(MyModule)
а затем импортировать как обычно. Я нахожу, что это немного больше проблем.
Если вы хотите получить доступ к функции foo при импорте модуля с помощью "using", вам нужно добавить" export foo " в заголовок модуля.
Если вы явно не загрузите файл (include("./Hello.jl")
) Julia ищет файлы модулей в каталогах, определенных в переменной LOAD_PATH.
посмотреть на этой странице.
существует новый ответ на этот вопрос с момента выхода Julia v0.7 и v1.0 это немного отличается. Мне просто нужно было это сделать, поэтому я решил опубликовать свои выводы здесь.
как уже объяснялось в других решениях, необходимо включить соответствующий скрипт, который определяет модуль. Однако, поскольку модуль не пакет, он не может быть загружен как пакет с тем же using
или import
команды, как это можно сделать в старой Julia версии.
Итак, основной.сценарий jl будет написан с относительным импортом, как это:
include("./Hello.jl")
using .Hello
foo()
Я нашел это объясняется просто в комментарий Стефана Карпинского по аналогичному вопросу. Как он описывает, ситуация также может стать более сложной при работе с подмодулями. The раздел документации по путям модулей тоже хорошая ссылка.