【问题标题】:Two Modules, both exporting the same name两个模块,都导出相同的名称
【发布时间】:2017-04-24 16:33:04
【问题描述】:

我想使用两个包:CorpusLoaders.jl 和 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 的依赖项。

我想知道如何为包的消费者解决问题——而不是包的创建者。

【问题讨论】:

  • 不,别说了,我的回答主要是关于用户案例。希望有同样问题的人需要比冯扬的答案更详细(或 lot :p )的人会在下面碰到我的问题:D 我只是想将我的 cmets 移到一个答案中以将它们从来了,呵呵。我现在将删除上面的 cmets 种子 :p 感谢您抽出宝贵时间进行编辑/澄清!

标签: module packages julia


【解决方案1】:

tl;dr在脚本中通过模块命名空间使用函数时限定函数,即CorpusLoader.sensekey() 和WordNet.sensekey()


说明

编辑后我对您的问题的理解(感谢您的澄清)是:

  • 您编写了一个名为CorpusLoaders.jl 的包,它导出了函数sensekey(::SenseTaggedWord)
  • 有一个外部包WordNet.jl,导出函数sensekey(::DB, ::Synset, ::Lemma)
  • 您有一个使用这两个模块的脚本。

并且您担心using 模块或直接“导入”函数可能会在您的脚本中产生歧义和/或错误,询问

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

我认为这源于对using 和import 彼此不同以及模块如何创建命名空间的轻微混淆。这在文档here 中有很好的解释。

本质上,答案是:

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

  2. 当您键入 using CorpusLoaders 时,您对 julia 的意思是“导入模块本身,和从它们的命名空间限定符中剥离的所有导出变量,并将它们带入 Main ”。请注意,这意味着您现在可以直接从 Main 访问 sensekey 作为函数,而无需命名空间限定符,和为 CorpusLoaders.sensekey(),因为您还导入了 模块 作为可以使用的变量。

如果你也尝试using 模块WordNet,julia 会非常合理地发出警告,它基本上说:

“你已经导入了两个同名的函数。我不能只是去掉它们的命名空间,因为这可能会在某些情况下产生问题(即使在你的情况下它不会因为它们有不同的签名,但是我一般不可能知道这一点)。如果你想使用这些函数中的任何一个,请使用它们适当的命名空间限定符“。

所以,2.的解决方案是:

  • 你要么做

    using CorpusLoaders;
    using WordNet;
    

    ,忽略警告,像往常一样在 Main 命名空间中导入所有其他导出的变量,并在每次需要在脚本中使用它们时直接通过它们的模块以 CorpusLoaders.sensekey() 和 WordNet.sensekey() 访问这些特定函数,或者

  • 通过这样做,您始终可以清楚地消除两个模块的歧义

    import CorpusLoaders;
    import WordNet;
    

    并适当地限定所有变量,或

  • 在这种函数签名不冲突的特殊情况下,如果您真的希望能够在没有命名空间限定符的情况下使用该函数,而是依赖多个调度,您可以这样做就像风扬建议的那样:

     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()

【讨论】:

    【解决方案2】:

    在这种情况下,功能不冲突,但一般情况下无法保证。稍后加载的包可能会将方法添加到将发生冲突的函数之一。因此,要能够将sensekey 用于这两个软件包,需要一些额外的保证和限制。

    一种方法是忽略这两个包的sensekey,而是提供您自己的,分派到正确的包:

    sensekey(x) = CorpusLoaders.sensekey(x)
    sensekey(x, y, z) = WordNet.sensekey(x,y,z)
    

    【讨论】:

      【解决方案3】:

      我实现了@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)
      

      通用函数 sensekey 的 2 种方法:

      • sensekey(db::WordNet.DB, ss::WordNet.Synset, lem::WordNet.Lemma)
      • sensekey(saword::CorpusLoaders.Semcor.SenseAnnotatedWord

      如果你想获得真正的闪光,你也可以重新导出 DocString。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-04
        • 2013-12-03
        • 2012-01-31
        • 1970-01-01
        • 1970-01-01
        • 2020-03-02
        相关资源
        最近更新 更多