Julia 模块与包管理
模块(module)用于把代码组织成独立的命名空间,避免命名冲突;包(package)则是打包发布的模块,通过内置的包管理器 Pkg 安装。
定义模块
使用 module ... end 定义模块:
实例
module MyModule
# export 声明对外公开的名字(可以写在模块任意位置)
export myfunc, PI
const PI = 3.14159
myfunc(x) = x * 2
# 没有导出的名字属于模块私有
helper(x) = x + 1
end
# export 声明对外公开的名字(可以写在模块任意位置)
export myfunc, PI
const PI = 3.14159
myfunc(x) = x * 2
# 没有导出的名字属于模块私有
helper(x) = x + 1
end
访问模块成员的三种方式
实例
# 方式一:通过模块名限定访问(总是可用)
julia> MyModule.PI
3.14159
julia> MyModule.myfunc(10)
20
# 方式二:using 导入模块名和所有 export 的名字
julia> using .MyModule
julia> myfunc(10) # 导出的名字可以直接用
20
julia> helper(10) # 未导出的名字不能用,需要 MyModule.helper
ERROR: UndefVarError: `helper` not defined in `Main`
# 方式三:import 只导入模块名本身
julia> import .MyModule
julia> MyModule.myfunc(1)
2
julia> MyModule.PI
3.14159
julia> MyModule.myfunc(10)
20
# 方式二:using 导入模块名和所有 export 的名字
julia> using .MyModule
julia> myfunc(10) # 导出的名字可以直接用
20
julia> helper(10) # 未导出的名字不能用,需要 MyModule.helper
ERROR: UndefVarError: `helper` not defined in `Main`
# 方式三:import 只导入模块名本身
julia> import .MyModule
julia> MyModule.myfunc(1)
2
using 与 import 的区别总结:
| 语句 | 效果 | 能否无限定扩展方法 |
|---|---|---|
using M | 导入模块名 + 全部导出名字 | 不能(需要写 M.f) |
import M | 只导入模块名 | 不能 |
import M: f | 只导入名字 f | 可以,直接写 f 新方法 |
using M: f | 只导入名字 f | 不能 |
注意:上面的例子中 using .MyModule 里的点表示"当前作用域中定义的模块"。从 REPL 或脚本中加载本地模块必须加点前缀;从已安装的包加载则直接写包名,如
using Example。
include 与多文件模块
实际项目中,模块代码通常拆分到多个文件,用 include() 组装:
实例
module MyPkg
include("types.jl") # 把文件内容在当前模块中执行
include("functions.jl")
end
include("types.jl") # 把文件内容在当前模块中执行
include("functions.jl")
end
标准模块
| 模块 | 说明 |
|---|---|
Main | 顶层模块,REPL 和脚本默认运行在 Main 中 |
Base | 基础库,默认自动可用(sum、push! 等都在这里) |
Core | 语言内置功能,编译器相关 |
| 标准库模块 | 随 Julia 附带、无需安装,如 Dates、Test、LinearAlgebra 等,需要 using 后使用 |
包管理器 Pkg
Julia 自带包管理器 Pkg,可以在 REPL 的 Pkg 模式中使用(输入 ] 进入),也可以通过 using Pkg 在代码中调用。
Pkg 模式常用命令
| 命令 | 功能 |
|---|---|
add 包名 | 安装包 |
rm 包名 | 卸载包 |
update 包名 | 更新包(无参数更新全部) |
status | 查看当前环境安装了哪些包 |
activate 路径 | 激活(切换)当前环境 |
test 包名 | 运行包的测试 |
precompile | 预编译所有依赖 |
实例:安装并使用第三方包
# 1. 按 ] 进入 Pkg 模式,安装 Example 包
(@v1.10) pkg> add Example
# 2. 按退格键回到 Julia 模式,加载并使用
julia> using Example
julia> hello("RUNOOB")
"Hello, RUNOOB"
(@v1.10) pkg> add Example
# 2. 按退格键回到 Julia 模式,加载并使用
julia> using Example
julia> hello("RUNOOB")
"Hello, RUNOOB"
代码中等价的写法:
实例
using Pkg
Pkg.add("Example") # 安装包
Pkg.status() # 查看已安装的包
Pkg.add("Example") # 安装包
Pkg.status() # 查看已安装的包
使用标准库
标准库随 Julia 一起安装,直接 using 即可,不需要 add:
实例
julia> using LinearAlgebra
julia> I # 单位矩阵记号
UniformScaling{Bool}(true)
julia> [1 2; 3 4] * I
2×2 Matrix{Int64}:
1 2
3 4
julia> I # 单位矩阵记号
UniformScaling{Bool}(true)
julia> [1 2; 3 4] * I
2×2 Matrix{Int64}:
1 2
3 4
环境(Environment)
Julia 的包安装在环境中。每个环境由一个项目目录描述,包含两个文件:
| 文件 | 作用 |
|---|---|
Project.toml | 项目的直接依赖列表和兼容性约束,应提交到版本库 |
Manifest.toml | 完整的依赖图快照(包括间接依赖的精确版本),保证环境可复现 |
创建并使用项目环境
实例
# 1. 在终端创建项目目录
$ mkdir myproject && cd myproject
# 2. 启动 Julia,激活当前目录为环境
$ julia
julia> using Pkg
julia> Pkg.activate(".")
Activating project at `~/myproject`
# 3. 安装依赖,会生成 Project.toml 和 Manifest.toml
julia> Pkg.add("JSON")
# 4. 以后只要在这个目录下启动 Julia,就自动使用这个环境
$ mkdir myproject && cd myproject
# 2. 启动 Julia,激活当前目录为环境
$ julia
julia> using Pkg
julia> Pkg.activate(".")
Activating project at `~/myproject`
# 3. 安装依赖,会生成 Project.toml 和 Manifest.toml
julia> Pkg.add("JSON")
# 4. 以后只要在这个目录下启动 Julia,就自动使用这个环境
最佳实践:不要把所有包装进默认环境(@v1.10),而是为每个项目建独立环境。这样不同项目可以使用同一个包的不同版本,互不干扰,还能通过提交 Project.toml 让别人精确复现你的环境。
开发自己的包
用 Pkg 可以快速生成一个包的骨架:
julia> ] generate MyPkg
Generating project MyPkg:
MyPkg/Project.toml
MyPkg/src/MyPkg.jl
生成的包主文件结构如下:
MyPkg/src/MyPkg.jl 文件
module MyPkg
# export 列出对外提供的函数
export greet
# 一个示例函数
greet() = "Hello from MyPkg!"
end
# export 列出对外提供的函数
export greet
# 一个示例函数
greet() = "Hello from MyPkg!"
end
在包目录下启动 Julia 并激活环境后,就可以直接 using 自己的包:
julia> ] activate MyPkg julia> using MyPkg julia> greet() "Hello from MyPkg!"
扩展 Base 函数时的 import 写法
在自己包里给 Base 的函数添加新方法时,建议用 import 明确声明:
实例
module MyPkg
import Base: show # import 后才能给 show 添加新方法
struct Temp
celsius::Float64
end
# 为自定义类型扩展 Base.show
function Base.show(io::IO, t::Temp)
print(io, "$(t.celsius)°C")
end
end
import Base: show # import 后才能给 show 添加新方法
struct Temp
celsius::Float64
end
# 为自定义类型扩展 Base.show
function Base.show(io::IO, t::Temp)
print(io, "$(t.celsius)°C")
end
end
