两个模块,都导出相同的名称
Two Modules, both exporting the same name
我想使用两个包:CorpusLoaders.jl, and WordNet.jl
- CorpusLoaders.SemCor 导出
sensekey(::SenseTaggedWord)
- WordNet 导出
sensekey(::DB, ::Synset, ::Lemma)
我想同时使用这两种 sensekey
方法。
例如
对于一些项目的混合列表:mixedlist::Vector{Union{Tuple{SenseTaggedWord},Tuple{DB, Synset,Lemma}}
。
即列表中的项目是 SenseTaggedWord
的 1 元组和 DB
、Synset
和 Lemma
.
的 3 元组的混合
for item in mixedlist
println(sensekey(item...)
end
应该可以。
这个例子有点滑稽,因为我为什么要这样混合它们。
但是,希望它能说明一般情况下的问题。
尝试 using CorpusLoaders.SemCor, WordNet
在 WARNING: both WordNet and Semcor export "sensekey"; uses of it in module Main must be qualified.
中引入两个结果
手动导入两者:import CorpusLoaders.SemCor.sensekey; import WordNet.sensekey
结果 WARNING: ignoring conflicting import of Semcor.sensekey into Main
可以做什么?我想要他们两个,他们并没有真正冲突,由于多重调度。
鉴于 CorpusLoaders.jl 是我正在编写的一个包,我确实有更多的选择,因为我可以让我的 CorpusLoaders.jl 依赖于 WordNet.jl。
如果我做了那么我可以在 CorpusLoaders.jl
中说
import WordNet
function WordNet.sensekey(s::SenseTaggedWord)...
这将使它们都起作用。
但这意味着需要将 WordNet 作为 CorpusLoaders 的依赖项。
而且我想知道如何为包的消费者解决问题 -- 而不是作为包的创建者。
在这种情况下,功能不会冲突,但一般情况下无法保证。稍后加载的包可能会向其中一个会发生冲突的函数添加方法。因此,为了能够对这两个包使用 sensekey
需要一些额外的保证和限制。
实现此目的的一种方法是忽略两个包的 sensekey
,而是提供您自己的包,分发到正确的包:
sensekey(x) = CorpusLoaders.sensekey(x)
sensekey(x, y, z) = WordNet.sensekey(x,y,z)
我实现了@Fengyang Wang说的,
作为函数:
function importfrom(moduleinstance::Module, functionname::Symbol, argtypes::Tuple)
meths = methods(moduleinstance.(functionname), argtypes)
importfrom(moduleinstance, functionname, meths)
end
function importfrom(moduleinstance::Module, functionname::Symbol)
meths = methods(moduleinstance.(functionname))
importfrom(moduleinstance, functionname, meths)
end
function importfrom(moduleinstance::Module, functionname::Symbol, meths::Base.MethodList)
for mt in meths
paramnames = collect(mt.lambda_template.slotnames[2:end])
paramtypes = collect(mt.sig.parameters[2:end])
paramsig = ((n,t)->Expr(:(::),n,t)).(paramnames, paramtypes)
funcdec = Expr(:(=),
Expr(:call, functionname, paramsig...),
Expr(:call, :($moduleinstance.$functionname), paramnames...)
)
current_module().eval(funcdec) #Runs at global scope, from calling module
end
end
呼叫:
using WordNet
using CorpusLoaders.Semcor
importfrom(CorpusLoaders.Semcor, :sensekey)
importfrom(WordNet, :sensekey)
methods(sensekey)
2 种通用函数 sensekey 的方法:
- sensekey(db::WordNet.DB, ss::WordNet.Synset, lem::WordNet.引理)
- sensekey(saword::CorpusLoaders.Semcor.SenseAnnotatedWord
如果你想获得真正的闪光,你也可以重新导出 DocString。
tl;dr 通过它们的模块名称空间在脚本中使用它们时限定函数,即 CorpusLoader.sensekey()
和 WordNet.sensekey()
解释
编辑后我对你的问题的理解(感谢你的澄清)是:
- 您编写了一个名为
CorpusLoaders.jl
的程序包,它导出函数 sensekey(::SenseTaggedWord)
- 有一个名为
WordNet.jl
的外部包,它导出函数 sensekey(::DB, ::Synset, ::Lemma)
- 您有一个同时使用这两个模块的脚本。
并且您担心直接 using
模块或 "importing" 函数可能会在您的脚本中产生歧义和/或错误,询问
- 如何编写我的
CorpusLoaders
包以防止与其他包发生潜在冲突,以及
- 如何编写脚本以明确区分这两个函数,同时仍允许使用它们?
我认为这源于对 using
和 import
之间的区别以及模块如何创建命名空间的轻微混淆。这在文档 here.
中有很好的解释
本质上,答案是:
您不必担心从您的模块中导出的内容会与其他模块发生冲突。这就是模块的用途:您正在创建一个命名空间,它将 "qualify" 所有导出的变量,例如CorpusLoaders.sensekey(::SenseTaggedWord)
.
当您键入 using CorpusLoaders
时,您对 julia 说的是 "import the module itself, and all the exported variables stripped from their namespace qualifier, and bring them into Main"。请注意,这意味着您现在可以直接从 Main 访问 sensekey
作为函数而无需名称空间限定符, 和 作为 CorpusLoaders.sensekey()
,因为您还导入了module 作为一个变量你可以使用。
如果您随后也尝试 using
模块 WordNet
,julia 会非常合理地发出警告,其本质上是:
"You've imported two functions that have the same name. I can't just strip their namespace off because that could create problems in some scenarios (even though in your case it wouldn't because they have different signatures, but I couldn't possibly know this in general). If you want to use either of these functions, please do so using their appropriate namespace qualifier".
因此,2. 的解决方案是:
你要么
using CorpusLoaders;
using WordNet;
,无视警告,像往常一样在 Main 命名空间中导入所有其他导出的变量,并在每次需要使用时通过它们的模块直接访问这些特定函数,如 CorpusLoaders.sensekey()
和 WordNet.sensekey()
它们在您的脚本中,或
你可以通过
始终清楚地消除两个模块的歧义
import CorpusLoaders;
import WordNet;
并适当限定 所有 变量,或
在这种函数签名不冲突的特殊情况下,如果您真的希望能够在没有命名空间限定符的情况下使用该函数, 依赖于多重分派,你可以像 FengYang 建议的那样做:
import CorpusLoaders;
import WordNet;
sensekey(a::SenseTaggedWord) = CorpusLoader.sensekey(a);
sensekey(a::DB, b::Synset, c::Lemma) = WordNet.sensekey(a, b, c);
本质上是一个 new 函数,定义在 Main 模块上,充当两个命名空间限定函数的包装器。
最后,这一切都归结为使用 using
与 import
和适合您的特定代码的命名空间。 :)
作为附录,使用长名称空间限定符(如 CorpusLoader
和 WordNet
时,代码可能会变得非常笨拙。 julia 没有类似 python 的 import numpy as np
、 但同时 模块成为工作空间中的简单变量,因此创建别名很简单为他们。所以你可以这样做:
import CorpusLoaders; const cl = CorpusLoaders;
import Wordnet; const wn = WordNet;
# ... code using both cl.sensekey() and wn.sensekey()
我想使用两个包:CorpusLoaders.jl, and WordNet.jl
- CorpusLoaders.SemCor 导出
sensekey(::SenseTaggedWord)
- WordNet 导出
sensekey(::DB, ::Synset, ::Lemma)
我想同时使用这两种 sensekey
方法。
例如
对于一些项目的混合列表:mixedlist::Vector{Union{Tuple{SenseTaggedWord},Tuple{DB, Synset,Lemma}}
。
即列表中的项目是 SenseTaggedWord
的 1 元组和 DB
、Synset
和 Lemma
.
for item in mixedlist
println(sensekey(item...)
end
应该可以。 这个例子有点滑稽,因为我为什么要这样混合它们。 但是,希望它能说明一般情况下的问题。
尝试 using CorpusLoaders.SemCor, WordNet
在 WARNING: both WordNet and Semcor export "sensekey"; uses of it in module Main must be qualified.
手动导入两者:import CorpusLoaders.SemCor.sensekey; import WordNet.sensekey
结果 WARNING: ignoring conflicting import of Semcor.sensekey into Main
可以做什么?我想要他们两个,他们并没有真正冲突,由于多重调度。
鉴于 CorpusLoaders.jl 是我正在编写的一个包,我确实有更多的选择,因为我可以让我的 CorpusLoaders.jl 依赖于 WordNet.jl。 如果我做了那么我可以在 CorpusLoaders.jl
中说 import WordNet
function WordNet.sensekey(s::SenseTaggedWord)...
这将使它们都起作用。 但这意味着需要将 WordNet 作为 CorpusLoaders 的依赖项。
而且我想知道如何为包的消费者解决问题 -- 而不是作为包的创建者。
在这种情况下,功能不会冲突,但一般情况下无法保证。稍后加载的包可能会向其中一个会发生冲突的函数添加方法。因此,为了能够对这两个包使用 sensekey
需要一些额外的保证和限制。
实现此目的的一种方法是忽略两个包的 sensekey
,而是提供您自己的包,分发到正确的包:
sensekey(x) = CorpusLoaders.sensekey(x)
sensekey(x, y, z) = WordNet.sensekey(x,y,z)
我实现了@Fengyang Wang说的, 作为函数:
function importfrom(moduleinstance::Module, functionname::Symbol, argtypes::Tuple)
meths = methods(moduleinstance.(functionname), argtypes)
importfrom(moduleinstance, functionname, meths)
end
function importfrom(moduleinstance::Module, functionname::Symbol)
meths = methods(moduleinstance.(functionname))
importfrom(moduleinstance, functionname, meths)
end
function importfrom(moduleinstance::Module, functionname::Symbol, meths::Base.MethodList)
for mt in meths
paramnames = collect(mt.lambda_template.slotnames[2:end])
paramtypes = collect(mt.sig.parameters[2:end])
paramsig = ((n,t)->Expr(:(::),n,t)).(paramnames, paramtypes)
funcdec = Expr(:(=),
Expr(:call, functionname, paramsig...),
Expr(:call, :($moduleinstance.$functionname), paramnames...)
)
current_module().eval(funcdec) #Runs at global scope, from calling module
end
end
呼叫:
using WordNet
using CorpusLoaders.Semcor
importfrom(CorpusLoaders.Semcor, :sensekey)
importfrom(WordNet, :sensekey)
methods(sensekey)
2 种通用函数 sensekey 的方法:
- sensekey(db::WordNet.DB, ss::WordNet.Synset, lem::WordNet.引理)
- sensekey(saword::CorpusLoaders.Semcor.SenseAnnotatedWord
如果你想获得真正的闪光,你也可以重新导出 DocString。
tl;dr 通过它们的模块名称空间在脚本中使用它们时限定函数,即 CorpusLoader.sensekey()
和 WordNet.sensekey()
解释
编辑后我对你的问题的理解(感谢你的澄清)是:
- 您编写了一个名为
CorpusLoaders.jl
的程序包,它导出函数sensekey(::SenseTaggedWord)
- 有一个名为
WordNet.jl
的外部包,它导出函数sensekey(::DB, ::Synset, ::Lemma)
- 您有一个同时使用这两个模块的脚本。
并且您担心直接 using
模块或 "importing" 函数可能会在您的脚本中产生歧义和/或错误,询问
- 如何编写我的
CorpusLoaders
包以防止与其他包发生潜在冲突,以及 - 如何编写脚本以明确区分这两个函数,同时仍允许使用它们?
我认为这源于对 using
和 import
之间的区别以及模块如何创建命名空间的轻微混淆。这在文档 here.
本质上,答案是:
您不必担心从您的模块中导出的内容会与其他模块发生冲突。这就是模块的用途:您正在创建一个命名空间,它将 "qualify" 所有导出的变量,例如
CorpusLoaders.sensekey(::SenseTaggedWord)
.当您键入
using CorpusLoaders
时,您对 julia 说的是 "import the module itself, and all the exported variables stripped from their namespace qualifier, and bring them into Main"。请注意,这意味着您现在可以直接从 Main 访问sensekey
作为函数而无需名称空间限定符, 和 作为CorpusLoaders.sensekey()
,因为您还导入了module 作为一个变量你可以使用。
如果您随后也尝试 using
模块 WordNet
,julia 会非常合理地发出警告,其本质上是:
"You've imported two functions that have the same name. I can't just strip their namespace off because that could create problems in some scenarios (even though in your case it wouldn't because they have different signatures, but I couldn't possibly know this in general). If you want to use either of these functions, please do so using their appropriate namespace qualifier".
因此,2. 的解决方案是:
你要么
using CorpusLoaders; using WordNet;
,无视警告,像往常一样在 Main 命名空间中导入所有其他导出的变量,并在每次需要使用时通过它们的模块直接访问这些特定函数,如
CorpusLoaders.sensekey()
和WordNet.sensekey()
它们在您的脚本中,或你可以通过
始终清楚地消除两个模块的歧义import CorpusLoaders; import WordNet;
并适当限定 所有 变量,或
在这种函数签名不冲突的特殊情况下,如果您真的希望能够在没有命名空间限定符的情况下使用该函数, 依赖于多重分派,你可以像 FengYang 建议的那样做:
import CorpusLoaders; import WordNet; sensekey(a::SenseTaggedWord) = CorpusLoader.sensekey(a); sensekey(a::DB, b::Synset, c::Lemma) = WordNet.sensekey(a, b, c);
本质上是一个 new 函数,定义在 Main 模块上,充当两个命名空间限定函数的包装器。
最后,这一切都归结为使用 using
与 import
和适合您的特定代码的命名空间。 :)
作为附录,使用长名称空间限定符(如
CorpusLoader
和 WordNet
时,代码可能会变得非常笨拙。 julia 没有类似 python 的 import numpy as np
、 但同时 模块成为工作空间中的简单变量,因此创建别名很简单为他们。所以你可以这样做:
import CorpusLoaders; const cl = CorpusLoaders;
import Wordnet; const wn = WordNet;
# ... code using both cl.sensekey() and wn.sensekey()