Julia子模块导出函数被遮蔽的原因及解决方法
现象是否符合设计预期
这个现象完全符合Julia的官方设计规则,不属于语言bug。
核心原因是Julia的模块作用域绑定规则:
- 使用
using导入其他模块的导出名称时,只是把该名称引入当前模块的命名空间,如果你在当前模块直接对同名名称做赋值/定义操作,会在当前模块创建一个全新的本地绑定,这个新绑定会直接遮蔽之前导入的同名绑定,不会自动给导入的原有函数追加方法。 - 示例里
using .Bar之后,写f(::String) = "abc"本质是在Foo模块新建了一个独立的f函数对象,和Bar模块里定义的f完全不是同一个;之前从Bar导入的f(带有f(::Int)方法)已经被新的本地f遮蔽,无法被直接访问。 - 调用
g()能正常运行,是因为传入的是String类型参数,刚好匹配本地f的f(::String)方法;调用h()时传入Int类型参数,本地f没有对应方法,又访问不到被遮蔽的Bar里的f(::Int),自然会抛出方法错误。
修复方案
根据实际需求选以下任意一种方案即可:
- 方案1:显式为导入的函数追加方法,不新建本地绑定
定义String类型的f方法时,明确指定是给Bar模块的f追加方法,就不会触发新建绑定的遮蔽逻辑:
module Foo export g, h module Bar export f f(::Int) = 2 end using .Bar # 显式指明为Bar.f追加方法,不会新建本地f绑定 Bar.f(::String) = "abc" g() = f("a") h() = f(12) end
此时调用g()返回"abc",h()返回2,两个方法同属Bar模块定义的f函数对象。
- 方案2:使用
import替代using导入需要扩展方法的函数
Julia中import导入的名称允许直接在当前模块追加方法,不会创建新的本地绑定:
module Foo export g, h module Bar export f f(::Int) = 2 end # 用import显式导入f,后续定义f方法会直接追加到原函数上 import .Bar: f f(::String) = "abc" g() = f("a") h() = f(12) end
运行效果和方案1完全一致。
- 方案3:如果确实需要在Foo模块保留独立的f函数(不需要和Bar的f合并),只需要在调用Bar的f时显式指定模块路径即可,比如把
h()的定义改成h() = Bar.f(12),就会绕过本地f的遮蔽直接调用Bar模块的对应方法。
注意:很多刚接触Julia的开发者会误以为同名称的函数会自动合并方法,实际上Julia的方法是绑定到具体函数对象上的,不同作用域下同名的函数如果是独立创建的,互相之间完全隔离,这也是出现这类遮蔽问题的根本原因。
内容的提问来源于stack exchange,提问作者Fernando
相关产品推荐
相关产品推荐

