编辑后如何在活动的 Julia 会话中重新加载模块?
Posted
技术标签:
【中文标题】编辑后如何在活动的 Julia 会话中重新加载模块?【英文标题】:How do I reload a module in an active Julia session after an edit? 【发布时间】:2014-09-21 15:01:00 【问题描述】:2018 年更新:请务必查看所有回复,因为多年来,此问题的答案已多次更改。在本次更新时,Revise.jl
答案可能是最好的解决方案。
我有一个文件“/SomeAbsolutePath/ctbTestModule.jl”,其内容是:
module ctbTestModule
export f1
f1(x) = x + 1
end
我在运行“~/.juliarc.jl”的终端中启动 Julia。启动代码包括以下行:
push!(LOAD_PATH, "/SomeAbsolutePath/")
因此我可以立即在 Julia 控制台中输入:
using ctbTestModule
加载我的模块。正如预期的那样,f1(1)
返回2
。现在我突然决定要编辑f1
。我在编辑器中打开“/SomeAbsolutePath/ctbTestModule.jl”,并将内容更改为:
module ctbTestModule
export f1
f1(x) = x + 2
end
我现在尝试在我的活动 Julia 会话中重新加载模块。我试试
using ctbTestModule
但f1(1)
仍然返回2
。接下来我试试:
reload("ctbTestModule")
如建议的here,但f1(1)
仍返回2
。最后,我尝试:
include("/SomeAbsolutePath/ctbTestModule.jl")
按照here 的建议,这不是理想的,因为我必须输入完整的绝对路径,因为当前目录可能不是“/SomeAbsolutePath”。我收到警告消息Warning: replacing module ctbTestModule
,这听起来很有希望,但f1(1)
仍然返回2
。
如果我关闭当前的 Julia 会话,启动一个新会话,然后输入 using ctbTestModule
,我现在会得到所需的行为,即 f1(1)
返回 3
。但显然我想这样做 不重新启动 Julia。
那么,我做错了什么?
其他详细信息:Ubuntu 14.04 上的 Julia v0.2。
【问题讨论】:
感谢您提供 2018 年更新。仅仅因为workspace()
在 Julia 中存在已经很长时间了,我现在甚至接受 miguelmorin 的答案,而不是以前接受的答案,该答案后来已被弃用。
@NoseKnowsAll 老实说,我不确定在这种情况下应该采取什么样的礼仪。接受的答案不一定是正确的。相反,常见问题解答指出它是提问者认为最有帮助的一个。对我来说,这是目前公认的答案。鉴于我在帖子顶部的 2018 年更新将读者引向了正确的方向,我认为最好保持现状。
【参考方案1】:
这个问题的基础是重新加载模块的汇合,但无法重新定义模块中的东西Main(see the documentation here)——即是at least until the new function workspace() was made available,2014 年 7 月 13 日。最新版本的 0.3 预发行版应该有它。
工作区()之前
考虑以下简单的模块
module TstMod
export f
function f()
return 1
end
end
那就用吧....
julia> using TstMod
julia> f()
1
如果函数 f() 更改为 return 2 并重新加载模块,则 f 实际上已更新。但未在模块 Main 中重新定义。
julia> reload("TstMod")
Warning: replacing module TstMod
julia> TstMod.f()
2
julia> f()
1
以下警告使问题更清楚
julia> using TstMod
Warning: using TstMod.f in module Main conflicts with an existing identifier.
julia> using TstMod.f
Warning: ignoring conflicting import of TstMod.f into Main
使用工作区()
但是,新函数 workspace() 会清除 Main 以准备重新加载 TstMod
julia> workspace()
julia> reload("TstMod")
julia> using TstMod
julia> f()
2
此外,之前的 Main 存储为 LastMain
julia> whos()
Base Module
Core Module
LastMain Module
Main Module
TstMod Module
ans Nothing
julia> LastMain.f()
1
【讨论】:
+1+勾选 非常感谢。一旦 *** 允许我,就会回来奖励赏金:-)clear!()
和 clear!(:TstMod)
对我来说失败了 ERROR: invalid redefinition of constant TstMod
。我不知道如何使用Base.Distributed.clear!
的documentation。 @PatrickT,你会如何使用它?
2020年有什么办法解决这个问题?发布的答案似乎都不起作用(未定义重新加载功能,1.5.3 中的工作区也未定义)。似乎是 IDE 的一个非常基本且重要的功能,用于清除环境或重新加载特定模块。
有人知道@cbartondock 问题的答案吗?
包裹 Revise
帮了我大忙【参考方案2】:
使用包Revise
,例如
Pkg.add("Revise") # do this only once
include("src/my_module.jl")
using Revise
import my_module
您可能需要在新的 REPL 会话中启动它。请注意使用import
而不是using
,因为using
不会重新定义Main
模块中的函数(正如@Maciek Leks 和@waTeim 所解释的那样)。
其他解决方案:与workspace()
相比,Revise.jl
的两个优点是 (1) 它更快,以及 (2) 它是面向未来的,因为 workspace()
已被弃用在 0.7 中,如 this GitHub issue 中所述:
julia> VERSION
v"0.7.0-DEV.3089"
julia> workspace()
ERROR: UndefVarError: workspace not defined
还有一位 GitHub 贡献者推荐 Revise.jl
:
我们是否应该添加一些消息,例如“工作区已弃用,请查看 Revise.jl”?
即使在 Julia 0.6.3 中,当一个模块调用其他模块时,workspace()
、import
和 reload
之前的三个解决方案也会失败,例如 DataFrames
。使用这三种方法,当我在同一个 REPL 中第二次调用该模块时,我得到了同样的错误:
ERROR: LoadError: MethodError: all(::DataFrames.##58#59, ::ArrayAny,1) is ambiguous. Candidates: ...
我也收到了很多警告信息,例如:
WARNING: Method definition macroexpand(Module, ANY) in module Compat at /Users/mmorin/.julia/v0.6/Compat/src/Compat.jl:87 overwritten in module Compat at /Users/mmorin/.julia/v0.6/Compat/src/Compat.jl:87.
重新启动 Julia 会话有效,但很麻烦。我找到了this issue in the Reexport package,有类似的错误信息:
MethodError: all(::Reexport.##2#6, ::ArrayAny,1) is ambiguous.
并听从了一位贡献者的建议:
不使用 workspace() 会发生这种情况吗?该函数因与包交互不佳而臭名昭著,这也是它在 0.7 中被弃用的部分原因。
【讨论】:
谢谢。我在问题顶部添加了一条横幅,建议读者查看所有答案(尤其是这个答案),因为过去几年最佳解决方案已多次更改。【参考方案3】:以我的拙见,更好的方法是从一开始就使用import
而不是using
用于报告的问题。
考虑模块:
module ModuleX1
export produce_text
produce_text() = begin
println("v1.0")
end
println("v1.0 loaded")
end
然后在 REPL 中:
julia> import ModuleX1
v1.0 loaded
julia> ModuleX1.produce_text()
v1.0
更新模块代码并保存:
module ModuleX1
export produce_text
produce_text() = begin
println("v2.0")
end
println("v2.0 loaded")
end
接下来,在 REPL 中:
julia> reload("ModuleX1")
Warning: replacing module ModuleX1
v2.0 loaded
julia> ModuleX1.produce_text()
v2.0
使用import
相对于using
的优势:
workspace()
使用import
优于using
的缺点:
已编辑:根据下面的对话,从“缺点...”中丢弃“对模块的完全访问权限,甚至对未导出的名称”。
【讨论】:
这很有趣。一个快速的问题:我认为只要我完全限定调用,我就可以通过using
完全访问模块中的非导出名称。这不正确吗?【参考方案4】:
workspace()
已被弃用。
您可以在活动的 REPL 会话中 reload("MyModule")
,它按预期工作:对包含 MyModule
的源文件所做的更改会反映在活动的 REPL 会话中。
这适用于import MyModule
或using MyModule
纳入范围的模块
【讨论】:
您需要使用reload("MyModule")
(即带引号),因为reload()
将字符串作为参数而不是模块。
我在src/my_module.jl
中有一个源文件。调用reload("src/my_module.jl")
会得到ERROR: LoadError: use 'include' instead of 'reload' to load source files
。这是因为斜线,因为source code 建议在源文件有斜线时包含它。没有斜线,它说ERROR: LoadError: ArgumentError: Module my_module not found in current path.
@bizzy,你如何将reload()
与子目录中的源文件一起使用?
@bizzy:找到了,我得把src/
加到LOAD_PATH
:push!(LOAD_PATH, "src/"); reload("my_module")
。【参考方案5】:
我想从头开始创建一个新模块,并尝试了 1.0 的不同答案,但没有得到满意的结果,但我发现以下方法对我有用:
来自我要用于我运行的项目的目录中的 Julia REPL
pkg> generate MyModule
这将创建一个类似以下结构的子目录:
MyModule
├── Project.toml
└── src
└── MyModule.jl
我将我的模块代码放在MyModule.jl
中。我切换到目录MyModule
(或在我的IDE中打开它)并添加一个文件Scratch.jl
,代码如下:
using Pkg
Pkg.activate(".")
using Revise
import MyModule # or using MyModule
然后我可以在下面添加我的代码进行测试,并且所有内容都会更新,而无需重新加载 REPL。
【讨论】:
这不意味着MyModule
(更像MyPackage
)现在将依赖Revise.jl
(所以安装你的包会自动安装Revise),即使后者只用于发展?以上是关于编辑后如何在活动的 Julia 会话中重新加载模块?的主要内容,如果未能解决你的问题,请参考以下文章
服务器断开连接后如何在 Spring MVC 中保持客户端会话处于活动状态