10 - 模块与包 / Modules & Packages
模块与包 / Modules & Packages
Lua 的模块系统极其简单——一个模块就是一个返回 table 的文件。通过 require 加载,通过 package.path 查找。
Lua’s module system is extremely simple — a module is a file that returns a table. Loaded with require, found via package.path.
🟢 基础 / Basics — 创建和使用模块
1. 创建模块 / Creating a Module
-- math_utils.lua — 一个简单的模块
local M = {}
function M.add(a, b)
return a + b
end
function M.multiply(a, b)
return a * b
end
function M.factorial(n)
if n <= 1 then return 1 end
return n * M.factorial(n - 1)
end
return M -- 返回模块 table
2. 使用模块 / Using a Module
-- main.lua
local math_utils = require("math_utils") -- 加载模块
print(math_utils.add(3, 5)) -- 8
print(math_utils.multiply(4, 7)) -- 28
print(math_utils.factorial(5)) -- 120
-- require 的搜索路径:
-- 1. package.loaded["math_utils"](已加载则直接返回)
-- 2. package.preload["math_utils"](预加载器)
-- 3. package.path 中的文件(.lua 文件)
-- 4. package.cpath 中的文件(.so/.dll 文件)
3. 模块路径 / Module Paths
-- 查看当前搜索路径
print(package.path) -- .lua 文件搜索路径
print(package.cpath) -- .so/.dll 文件搜索路径
-- 默认路径示例(Linux):
-- ./?.lua;/usr/local/share/lua/5.4/?.lua;/usr/local/share/lua/5.4/?/init.lua
-- require("foo") 会搜索:
-- ./foo.lua
-- /usr/local/share/lua/5.4/foo.lua
-- /usr/local/share/lua/5.4/foo/init.lua
-- 自定义路径
package.path = package.path .. ";./lib/?.lua;./lib/?/init.lua"
-- 点号转目录分隔符
-- require("lib.utils") → 搜索 lib/utils.lua
🟡 进阶 / Intermediate — 模块的高级用法
1. 模块缓存 / Module Caching
-- require 会缓存已加载的模块
-- require caches loaded modules in package.loaded
local m1 = require("math_utils")
local m2 = require("math_utils")
print(m1 == m2) -- true(同一个 table 引用)
-- 强制重新加载
package.loaded["math_utils"] = nil
local m3 = require("math_utils")
print(m1 == m3) -- false(新的 table)
-- 查看已加载的模块
for name, mod in pairs(package.loaded) do
print(name, type(mod))
end
2. 模块的内部私有性 / Module Privacy
-- 推荐模式:用 local 变量做私有成员
local M = {}
-- 私有变量(不暴露给外部)
local counter = 0
local function privateHelper()
counter = counter + 1
return counter
end
-- 公开接口
function M.getCount()
return counter
end
function M.increment()
return privateHelper()
end
return M
-- 外部只能通过 M 的公开方法访问
-- counter 和 privateHelper 对外部不可见
3. module() 函数(已废弃)/ Deprecated module()
-- Lua 5.1 曾有 module() 函数,现在不推荐使用
-- module("mymodule") -- 不要这样写!
-- 现代写法(简单明了):
local M = {}
-- ... 定义函数 ...
return M
4. Lua 调用 C 模块 / Lua Calling C Modules
-- C 模块通常编译为 .so 或 .dll 文件
-- 例如:local cjson = require("cjson")
-- C 模块的接口:
-- 1. 编写 C 代码,使用 Lua C API
-- 2. 编译为共享库 (.so)
-- 3. 放入 package.cpath 路径
-- 4. require("cjson") 即可
-- 查看可用的 C 模块路径
print(package.cpath)
-- 完整的 C 模块加载流程:
-- require("cjson")
-- 1. 检查 package.loaded["cjson"]
-- 2. 检查 package.preload["cjson"]
-- 3. 搜索 package.cpath,找到 cjson.so
-- 4. dlopen("cjson.so")
-- 5. 调用 luaopen_cjson() 函数
-- 6. 将返回值存入 package.loaded["cjson"]
🔴 高级 / Advanced — 沙箱与高级模块系统
1. 沙箱环境 / Sandbox Environment
-- 沙箱:限制代码可以访问的环境
local function createSandbox()
local env = {
print = print, -- 允许 print
tostring = tostring,
tonumber = tonumber,
type = type,
pairs = pairs,
ipairs = ipairs,
string = string,
math = math,
table = table,
-- 不暴露:io, os, debug, loadfile, dofile, require
}
return env
end
local function runSandboxed(code, env)
local fn, err = load(code, "sandbox", "t", env)
if not fn then return nil, err end
return pcall(fn)
end
-- 使用
local sandbox = createSandbox()
local ok, err = runSandboxed([[
print("Hello from sandbox!")
local x = math.sqrt(16)
print("sqrt(16) = " .. x)
os.execute("rm -rf /") -- 这行会报错:os 未定义
]], sandbox)
2. 环境隔离 / Environment Isolation
-- 每个模块可以有自己的环境
local function isolatedModule(code)
local env = setmetatable({}, {__index = _G}) -- 继承全局环境
local fn = load(code, "module", "t", env)
fn()
return env
end
local mod = isolatedModule([[
local x = 10
function hello() print("Hello!") end
export_x = x -- 导出到环境
]])
print(mod.export_x) -- 10
-- mod.hello() -- 可以调用
3. 模块热重载 / Hot Reload
-- 实现简单的模块热重载
local function reload(name)
-- 清除缓存
package.loaded[name] = nil
-- 可选:清除 package.searchpath 找到的文件的缓存
-- 重新 require
local ok, mod = pcall(require, name)
if ok then
print("Reloaded: " .. name)
return mod
else
print("Failed to reload " .. name .. ": " .. tostring(mod))
return nil
end
end
-- 使用场景:开发时修改模块后无需重启程序
-- local mylib = reload("mylib")
4. 模块初始化模式 / Module Initialization Patterns
-- 模式一:立即初始化
local M = {}
local config = {} -- 模块加载时立即初始化
function M.init() end
return M
-- 模式二:延迟初始化(首次使用时初始化)
local M = {}
local initialized = false
local function ensureInit()
if initialized then return end
initialized = true
-- 初始化操作
end
function M.doSomething()
ensureInit()
-- ...
end
return M
-- 模式三:工厂模式(每次 require 返回新实例)
local function create()
local state = {}
local M = {}
function M.get() return state end
function M.set(v) state = v end
return M
end
return create -- 返回工厂函数,而不是模块本身
-- 使用:local mymod = require("mymod")()
小结 / Summary
| 层级 | 你需要知道的 / What You Need to Know |
|---|---|
| 🟢 基础 | local M = {} … return M、require()、package.path |
| 🟡 进阶 | 模块缓存机制、package.loaded、私有变量、C 模块加载 |
| 🔴 高级 | 沙箱环境、环境隔离、热重载、初始化模式 |
下一章:协程 / Coroutines