两个模块,都导出相同的名称

Two Modules, both exporting the same name

我想使用两个包:CorpusLoaders.jl, and WordNet.jl

我想同时使用这两种 sensekey 方法。

例如

对于一些项目的混合列表:mixedlist::Vector{Union{Tuple{SenseTaggedWord},Tuple{DB, Synset,Lemma}}。 即列表中的项目是 SenseTaggedWord 的 1 元组和 DBSynsetLemma.

的 3 元组的混合
for item in mixedlist
    println(sensekey(item...)
end

应该可以。 这个例子有点滑稽,因为我为什么要这样混合它们。 但是,希望它能说明一般情况下的问题。

尝试 using CorpusLoaders.SemCor, WordNetWARNING: 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" 函数可能会在您的脚本中产生歧义和/或错误,询问

  1. 如何编写我的 CorpusLoaders 包以防止与其他包发生潜在冲突,以及
  2. 如何编写脚本以明确区分这两个函数,同时仍允许使用它们?

我认为这源于对 usingimport 之间的区别以及模块如何创建命名空间的轻微混淆。这在文档 here.

中有很好的解释

本质上,答案是:

  1. 您不必担心从您的模块中导出的内容会与其他模块发生冲突。这就是模块的用途:您正在创建一个命名空间,它将 "qualify" 所有导出的变量,例如CorpusLoaders.sensekey(::SenseTaggedWord).

  2. 当您键入 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 模块上,充当两个命名空间限定函数的包装器。

最后,这一切都归结为使用 usingimport 和适合您的特定代码的命名空间。 :)


作为附录,使用长名称空间限定符(如 CorpusLoaderWordNet 时,代码可能会变得非常笨拙。 julia 没有类似 python 的 import numpy as np 但同时 模块成为工作空间中的简单变量,因此创建别名很简单为他们。所以你可以这样做:

import CorpusLoaders; const cl = CorpusLoaders;
import Wordnet;       const wn = WordNet;
# ... code using both cl.sensekey() and wn.sensekey()