Julia 学习笔记(五) | 模块开发 - 保姆级教程
唠唠闲话
今年参与了 OSPP’22 的 Julia 开源项目,期间学习了 Julia 模块从开发到测试的完整过程。借这个机会,整理这段时间学习到的知识。Julia 在开发提供的工具便捷丰富,且对新人尤其友好。
推荐阅读:
- B 站读文档系列 P5 - Julia 包实战
- Julia 短课 8 - Julia 开发工作流
- 官方文档 - Pkg 包管理
概要
本篇内容包括:
- 文件结构和文件作用
- 简易 Demo 及常用包管理命令
作为例子,可以参考个人参与维护的项目:QRCoders.jl 及 QRDecoders.jl.
项目开发通常用 Git 工具,所以这里假定已经有了一定的 Git 基础。如果不熟悉 Git,可以参考 廖雪峰的 Git 教程。
文件结构和作用
文件可以分三部分:
- 开源项目常用文件,比如
README.md
、LICENSE
等 - 项目代码,比如
src
,docs
和test
- 环境文件:
Project.toml
和Manifest.toml
其中环境文件是本节讨论重点。
文件结构
打开一个 Julia 包,查看文件结构,比如
# tree QRCoders.jl/ -L 2
QRCoders.jl/
├── docs
│ ├── make.jl
│ ├── Manifest.toml
│ ├── Project.toml
│ └── src
├── LICENSE
├── Manifest.toml
├── Project.toml
├── README.md
├── src
│ ├── QRCoders.jl
│ └── tables.jl
└── test├── runtests.jl└── tst_overall.jl
最外层有这几个文件、文件夹:
文件(夹) | 作用 |
---|---|
LICENSE
|
开源协议 |
README.md
|
包的介绍说明 |
docs
|
包的使用文档,用于生成网页 |
src
|
存放源代码的地方 |
test
|
测试代码 |
Manifest.toml
|
项目依赖的具体版本 |
Project.toml
|
项目依赖的包 |
一般开源仓库都有 LICENSE
和 README.md
。LICENSE
为开源协议,用得比较广泛的是 MIT LICENSE;README.md
用于介绍项目信息,使用方法、贡献者等等,打开 GitHub 仓库,直接看到的页面就是由 README.md
展示。
仓库通常还有
.gitignore
文件,用于忽略不需要上传的文件,比如测试过程产生的临时文件,或者使用 Jupyter 产生的.ipynb_checkpoints
文件等。
src/
, test/
和 docs
是 Julia 包约定或规定要有的文件夹:
src
存放源代码,且要求必须有模块的同名文件;假设模块名为QRCoders
,则必须存在文件src/QRCoders.jl
;当我们使用using/import
导入 Julia 包时,背后是在执行src/<模块名>.jl
文件test
可选,存放测试代码;test
目录下需存放runtests.jl
文件;当使用包管理模式执行test
命令时,会自动执行test/runtests.jl
的内容
docs
可选,用于生成使用文档;通常配合Documenter.jl
使用,并在docs
目录下存放make.jl
文件,用于生成网页
关于测试 test
和文档 docs
,我们在下篇单独展开介绍。
还有两个文件 Manifest.toml
和 Project.toml
,用于管理包依赖的,比较重要,接下来单独介绍。
环境文件
Project.toml
和 Manifest.toml
的几点比较:
- Pkg 模式下执行
instanitate
,将在当前环境所在目录生成Manifest.toml
和Project.toml
,用于记录包的依赖信息,比如
每次执行包的安装、删除、更新等操作时,Pkg 会自动更新这两个文件
模块主目录必须有
Project.toml
文件,而Manifest.toml
是可选的Mainifest.toml
可读性较差,通常只通过执行 Pkg 命令自动更改,不建议手动修改;而Project.toml
内容要简洁很多,可以手动维护Project.toml
记录项目的基本信息,一般来说是 Pkg 和程序员共同控制的内容;Manifest.toml
是包管理器 Pkg 基于Project.toml
生成的内容(以及执行包操作触发的修改),其记录了执行这个项目所需要的全部依赖的信息多数项目只提供
Project.toml
就足够了指定环境和依赖了,但一些特殊情况,可能需要提供Manifest.toml
,下一小节在介绍具体参数文档
docs
和测试test
也可以设置环境依赖,通过在相应目录添加Project.toml
(和Manifest.toml
)来设置从可复现性的角度来说, 从高可复现至低可复现依次是:
- 提供
Project.toml/Manifest.toml
: 常用于各种一次性项目 - 提供
Project.toml
:常用于工具箱开发 - 啥也没有:单纯的代码库,不能用
using
调用
- 提供
注:实践通常用
PkgTemplate
生成环境文件,而不需要自己手动编辑,但了解参数含义还是很有必要
Project.toml
推荐阅读:Pkg – Project.toml and Manifest.toml
以 QRCoders.jl
为例,查看 Project.toml
的参数:
name = "QRCoders"
uuid = "f42e9828-16f3-11ed-2883-9126170b272d"
authors = ["Jérémie Gillet <jie.gillet@gmail.com> and contributors"]
version = "1.0.1"[deps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"[compat]
FileIO = "1"
ImageCore = "0.8, 0.9"
ImageIO = "0.4, 0.5, 0.6"
julia = "1.3"[extras]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"[targets]
test = ["Test", "Random"]
文件包括了五个部分:
顶部是项目的基本信息
name
:模块名称,Julia 通过识别name
来确定当前环境名称;此外src/
目录下必须有以name
内容命名的.jl
文件uuid
全称 Universally Unique Identifier(通用唯一识别码),用于标识 Julia 包authors
作者信息version
当前模块的版本号,使用 Semantic Versioning(语义化版本)
deps
是当前项目依赖的包,使用pkg> add
添加环境依赖时会自动更新compact
是项目依赖的包的版本要求,需手动指定。按开发规范应为非自带包设置版本要求,并指定兼容的 Julia 版本extras
是额外的依赖,比如测试环境依赖的包。比如 QRCoders 在这里添加了测试环境需要的Test, Random
,因而省去了test
目录下的Project.toml
文件targets
似乎是按extras
对应出现的,目前了解不多
通常,只有 compact
要手动指定依赖版本,其他的都是自动生成,或者在用包管理器时自动更新。
语义化版本
在注册包时,可能会遇到关于版本号的相关问题,这里简单介绍一下,内容摘自 短课笔记-8。
代码工程中很容易遇到的一个问题就是版本冲突,例如: A 与 B 都是项目中需要的包, 而 A 与 B 都依赖于一个共同的包 C, 这时候如果 A 仅支持
C == 0.4.0
而 B 仅支持C == 1.0.0
的话,那么就出现了版本冲突现象。为了降低版本冲突的可能性,包管理器 Pkg 采用了语义化版本 Semantic Versioning。大概意思是说,所有版本分为四个主要类别: 主版本 major, 小版本 minor, 补丁版本 patch 以及构建版本 build。在 Julia 中大部分时候仅使用前三个版本。
版本格式:
主版本号.次版本号.修订号
,版本号递增规则如下:
- 主版本号:当你做了不兼容的 API 修改
- 次版本号:当你做了向下兼容的功能性新增
- 修订号:当你做了向下兼容的问题修正
先行版本号及版本编译信息可以加到 主版本号.次版本号.修订号
的后面,作为延伸。
基于 SemVer,我们可以做出类似于这样的假设:
如果 A 兼容
C == 1.0.0
, 那么 A 应该也兼容C == 1.0.1
和C == 1.1.0
,但未必兼容C == 2.0.0
。
为此,Project.toml
引入了 [compat]
块,例如:
FileIO = "1"
代表仅兼容1.x.y
版本的FileIO
ImageCore = "0.8, 0.9"
代表仅兼容0.8.x
和0.9.x
- 版本号还支持
"≥ 1.2.3"
等语法,相关规则参看 Version specifier format
此外,以 0 开始的版本通常表示仍在开发中,此时次版本不一定能向下兼容,比如 0.2
不一定能兼容 0.1
。
Manifest.toml
Mainfest.toml
由一系列格式如下的片段构成,记录了项目详细的依赖信息
[[QRCoders]]
deps = ["FileIO", "ImageCore", "ImageIO"]
git-tree-sha1 = "a7a56a2550dbea3b603b357adf81710385d1d3c7"
uuid = "f42e9828-16f3-11ed-2883-9126170b272d"
version = "1.0.1"
其中 git-tree-sha1
记录了该模块所在 commit 的哈希结果,其他参数与前边讨论类似。
多数时候,我们并不需要添加 Manifest.toml
来记录环境。但一些情况,比如依赖的模块未注册,或者依赖模块是在本地开发,就得提供 Manifest.toml
来保证环境的可复现。比较常见的两种情况:
安装未注册的
Github
包,比如pkg> add https://github.com/JuliaImages/QRCoders.jl
在
Manifest.toml
中会出现类似的片段[[QRCoders]] deps = ["FileIO", "ImageCore", "ImageIO"] git-tree-sha1 = "a7a56a2550dbea3b603b357adf81710385d1d3c7" repo-rev = "master" repo-url = "https://github.com/JuliaImages/QRCoders.jl" uuid = "f42e9828-16f3-11ed-2883-9126170b272d" version = "1.0.1"
其中
repo-rev
记录模块所在分支,repo-url
记录模块所在地址。当然不只是 GitHub 仓库,本地仓库或其他 Git 仓库也能用这种方式添加安装本地开发的包
# dev /path/to/your/package pkg> dev /home/rex/ospp/QRDecoders.jl
在
Manifest.toml
中出现的片段[[QRDecoders]] deps = ["FileIO", "ImageIO", "ImageTransformations", "QRCoders"] path = "/home/rex/ospp/QRDecoders.jl/" uuid = "d4999880-6331-4276-8b7d-7ee1f305cff8" version = "0.1.0"
以上两种情况均需要使用 Manifest.toml
来完整记录环境。
小结
本节介绍了 Julia 包的文件结构,包括
GitHub 仓库常用的
README.md
项目介绍LICENSE
开源协议.gitignore
忽略无关文件
Julia 代码文件:
src
存放源代码test
存放测试代码docs
存放文档
Julia 环境文件
Project.toml
项目依赖Manifest.toml
项目详细依赖
对多数包来说,只使用 Project.toml
记录环境,而将 Manifest.toml
放在 .gitignore
中忽略。但当使用未注册的包时,或有更严格的复现需求时,就得提供 Manifest.toml
来记录环境。
如果
Manifest.toml
已被.gitignore
设置了忽略,可以通过git add -f Manifest.toml
强制添加;相反的,如果Manifest.toml
已被添加,可以通过git rm --cached Manifest.toml
来取消添加。
简易教程
这节演示如何通过包管理创建模板,跟开发需求有关的更多使用 PkgTemplates
,我们下一篇再介绍。
Demo
作为示例,新建目录 TestPackage
,后续操作都在这里进行。
创建 git 环境
cd TestPackage # 进入目录 git init # 初始化 git
以下为 Git 仓库常用文件(可选)
# 取消对某些文件的追踪 touch .gitignore # 仓库介绍 touch README.md # 开源协议 touch LICENSE # 粘贴合适的协议
以当前目录作为开发环境启动 Julia
# julia --project=<环境位置> julia --project=.
等价地,可以先启动 Julia ,然后在包管理模式中使用
activate
切换开发环境# 先启动 Julia ] # 输入 ] 进入包管理模式 activate . # 启用当前目录作为开发环境
包管理模式下,使用
generate + 模块名
创建模板pkg> generate TestPackage
如下图,操作后
Project.toml
和src
文件夹已创建
打开文件并查看,
Project.toml
已自动生成了name
和uuid
等字段,其中authors
由当前环境的Git
信息生成name = "TestPackage" uuid = "83012822-a8b3-402d-b3e2-a8f809b7e3a3" authors = ["rex <1073853456@qq.com>"] version = "0.1.0"
src
代码内容module TestPackagegreet() = print("Hello World!") end # module
我们需要将
TestPackage
文件夹中的文件挪到模块主目录mv TestPackage/* ./ rmdir TestPackage
包管理模式下,使用
instantiate
命令初始化pkg> instantiate
该命令会根据
Project.toml
文件生成Manifest.toml
文件;当Project.toml
文件不存在时,会生成空白的Project.toml
和Manifest.toml
提交更改
git add --all git commit -m "initialize commit"
大功告成,一个简单的包已经创建好了!
包管理命令
包管理模式常用命令:
命令 | 说明 |
---|---|
add <模块名>
|
安装模块;若模块已安装,则根据环境文件执行更新操作 |
add <链接>
|
通过链接(地址)安装模块 |
rm/remove <模块名>
|
删除模块依赖 |
instantiate
|
实例化,也即更新 Project.toml, Manifest.toml 文件
|
test
|
执行 test/ 目录下的 runtests.jl
|
st/status
|
查看依赖信息 |
activate <环境目录>
|
将开发环境切换到指定目录,不加参数则切换到默认环境 |
generate <模块名称>
|
生成模板 |
update
|
更新环境依赖中的模块 |
dev <模块目录>
|
本地开发,添加本地模块作为依赖 |
build
|
构建依赖 |
precompile
|
环境预编译 |
简单来说,如果仓库没有 Project.toml
文件,则执行 instatiate
命令初始化。之后在执行 add, rm, update, dev
等操作时,仓库会自动更新 Project.toml
和 Manifest.toml
文件。
注:一些环境错误,比如执行
test
提示的文件错误,可能是Manifest.toml
的问题,如该文件非必要可将其删除再运行试试
其他知识点:
模块定义
module ExamplePackageexport greet, notdefinegreet() = print("Hello World!") end
其中
export
导出符号。当使用using <模块名>
时,这些符号将导入到当前环境。特别注意,export
只是导入符号而不做检查,哪怕符号对应的变量没有定义也能正常进行,且能够触发代码补全功能
导入模块除了
using
,也可以用import
,区别参看之前回答的一个帖子。
开发相关
Julia 提供了 Revise
包,可以实现模块热更新,即修改模块后无需重新使用 using
重新导入,也会自动更新模块变化。
但需要注意的是,Revise
只影响在它之后导入的模块,因此建议将其添加到 startup.jl
,每次启动 Julia 都会先执行这个脚本。
具体添加方式:直接编辑 ~/.julia/config/startup.jl
文件或者使用命令行
echo "using Revise" >> ~/.julia/config/startup.jl
其他知识点:
项目代码通常不止一个文件,当
src
下写了多个文件时,可以用include
命令将它们导入到主文件中## 主文件内 module ExamplePackagegreet() = print("Hello World!")include("temp.jl") # 导入文件 end
include
命令不仅适用于模块开发,普通场景的代码导入也是有效的在模块
module
内,引用内部模块可以略写模块名,比如module Outermodule Innerhi() = print("hi")end# 引用子模块using .Inner# 等价效果 using Outer.Inner end
变量及所在模块
- 使用
@which
可以查看变量所在模块 - 在 REPL 中定义的变量和函数属于全局环境
Main
- 使用
查看源码技巧:
- 在 Jupyter 下,使用
@which
还能获取函数源码的 GitHub 链接 - 使用
@edit func(para..)
可以查看func
关于该派发的源码 @edit
默认使用vim
作为编辑器,但可以用下边代码指定 vscode
ENV["EDITOR"] = "code"
也建议将这段代码写在
startup.jl
中,每次启动 Julia 都会执行该文件- 在 Jupyter 下,使用
模块开发还有些其他内容,比如 __init__
函数等,后续接触再进行补充。
以上是模块开发的第一部分知识,如果有什么建议或修改意见,欢迎在评论区指出~
Julia 学习笔记(五) | 模块开发 - 保姆级教程相关推荐
- 还不会小程序开发?适用于新手、前端、后端、全栈的小程序开发保姆级教程!
2017年,那个男人微信之父"龙哥",在公开课上提出"小程序".那时大家都还在享受移动互联网所带来的便捷体验,对于微信这个庞然大物所倡导的新物种显然是有点不知所 ...
- 微信小程序学习笔记(五) 云开发
1. 云开发简介 1.1 什么是云开发 微信云开发是微信团队联合腾讯云推出的专业的小程序开发服务. 开发者可以使用云开发快速开发小程序.小游戏.公众号网页等,并且原生打通微信开放能力. 开发者无需搭建 ...
- 高校疯传!法国TOP双硕算法专家匠心打造一套保姆级AI学习笔记并公开(保姆级/20G高清/PPT/代码)...
AI 显然是最近几年非常火的一个新技术方向,从几年前大家认识到 AI 的能力,到现在产业里已经在普遍的探讨 AI 如何落地了. 我们可以预言未来在很多的领域,很多的行业,AI 都会在里边起到重要的作用 ...
- mysql行级安全_MySQL学习笔记(五):MySQL表级锁和行级锁
一:概述 相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制.比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking ...
- ZYNQ-Linux开发之(五)Vivado工程搭建,设备树修改方法,复旦微电子fmql开发、vivado联合procise、IAR的linux系统开发-保姆级教程-非常详细
FMQL开发流程 整体开发流程如下: 具体开发流程: Vivado工程搭建 首先根据自己项目的功能需求,结合原理图,明确vivado工程中所需搭建的PS和PL部分的资源. 搭建使用的是vivado20 ...
- R · R 包开发 | 保姆级教程
R · R 包开发 一般在初始阶段我们都是使用别人的函数来完成大部分任务,那些函数大多来自 R 标准安装的包或者可以从 CRAN 下载的包. 安装新的包可以拓展 R 的功能.比如说,安装 ggplot ...
- Java之多线程学习笔记五 —— 多线程模拟龟兔赛跑
Java之多线程学习笔记五 -- 多线程模拟龟兔赛跑 参考教程B站狂神https://www.bilibili.com/video/BV1V4411p7EF package pers.ylw.less ...
- 前端学习笔记 HTML5 保姆级教程
HTML5 保姆级教程 前端学习路线: HTML→CSS→JavaScript→jQuery→HTML5→CSS3→ES6→Vue.js→webpack→Node.js 除了掌握这些技术,后期我还需要 ...
- 哈工大操作系统学习笔记五——内核级线程实现
哈工大os学习笔记五(内核级线程实现) 文章目录 哈工大os学习笔记五(内核级线程实现) 一. 中断入口.中断出口(前后两段) 1. 从int中断进入内核(中断入口第一段) 2.中断出口(最后一段) ...
最新文章
- IntelliJ IDEA 安装go插件
- Active Directory操作主机的转移 —图形操作
- C# - list数据填充到Dataset里
- 手把手教你搭建用户画像,数据分析效率提升百倍
- mysql5.7.18压缩包下载_Windows安装MySQL5.7教程
- pycharm 无法import PIL
- Could not create a sandbox extension for /
- 前端基础-html-视频标签的介绍
- MySql 随机读取多行数据并显示
- 计算机无法读取exe文件,电脑打不开exe文件怎么解决
- WEB3.0全球高峰论坛暨2022中民协分布式存储专委会新春团拜会即将隆重启幕
- 网站开发之HTML基础表格Table和表单Form(三)
- 高分屏更改Adobe Premier CC UI界面字体大小
- JVM-由常量池 运行时常量池 String intern方法想到的(三)之String内存模型
- sql语句中的right函数
- pip 如何设置代理
- MacBookPro恢复出厂系统
- 在Maxthon中使用Google网站加速器(转)
- Kettle 9.1 下载
- muParser公式库使用简介
热门文章
- Oracle - 基本入门
- android 6.0 vold shutdown流程
- html插入图像标签是什么,初学HTML 常见的标签(三) 插入类标签
- Angular4 失焦与点击冲突处理
- PHP中获取当前请求的URL
- IntelliJ IDEA 中 svn 修改服务器地址的方法 svn relocating working copy
- TC358840XBG HDMI 转 CSI 中文手册 精选资料分享
- vs2010打包解决“.NET Framework 3.5 SP1”
- zadig对于java的支持
- 马云:无论被拒绝多少次,请相信自己