编写模块文件
通过从 .lua 文件返回函数表来创建模块。
编写模块文件 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。
基本模块模式
Lua 的标准模块模式是:创建一个局部表 M,向其中添加函数和值,最后返回它。这个表就是模块的公共 API。文件中的其他局部变量都是私有的。
-- stringutils.lua
local M = {}
function M.trim(s)
return s:match("^%s*(.-)%s*$")
end
function M.split(s, sep)
local t = {}
for p in s:gmatch("[^"..sep.."]+") do t[#t+1]=p end
return t
end
return M
-- Usage:
-- local su = require("stringutils")
-- print(su.trim(" hello "))私有状态
在模块文件中使用 local 声明的变量是私有的——调用者无法访问它们。模块中的函数可以通过上值访问私有状态。这是 Lua 的封装机制。
-- counter.lua
local M = {}
local count = 0 -- private state
function M.increment(n)
count = count + (n or 1)
end
function M.reset()
count = 0
end
function M.get()
return count
end
return M带初始化的模块
有些模块需要初始化(例如配置和连接)。您可以将初始化代码放在模块级别(文件顶部),也可以放在显式的 M.init() 函数中。模块级别的方式会在第一次 require 时运行一次;init() 则需要显式调用。
-- cache.lua
local M = {}
local store = {} -- initialized at load time
local hits = 0
local misses = 0
function M.get(key)
if store[key] ~= nil then
hits = hits + 1
return store[key]
end
misses = misses + 1
return nil
end
function M.set(key, val) store[key] = val end
function M.stats() return {hits=hits, misses=misses} end
return M带类的模块
模块可以导出一个类:也就是包含构造函数的表。返回的模块表包含 new() 函数,也可以同时充当类的元表。
-- point.lua
local Point = {}
Point.__index = Point
function Point.new(x, y)
return setmetatable({x=x, y=y}, Point)
end
function Point:distance(other)
local dx, dy = self.x-other.x, self.y-other.y
return math.sqrt(dx*dx + dy*dy)
end
function Point:__tostring()
return string.format("(%g,%g)", self.x, self.y)
end
return Point
-- Usage:
-- local Point = require("point")
-- local p = Point.new(3, 4)模块常量
将常量添加到模块表中即可导出它们。按照惯例,常量使用大写形式。由于 Lua 没有 const,用户在技术上仍然可以修改它们,但大写命名表示“不要修改”。
-- colors.lua
local M = {}
M.RED = {r=255, g=0, b=0}
M.GREEN = {r=0, g=255, b=0}
M.BLUE = {r=0, g=0, b=255}
M.WHITE = {r=255, g=255, b=255}
M.BLACK = {r=0, g=0, b=0}
function M.toHex(c)
return string.format("#%02X%02X%02X", c.r, c.g, c.b)
end
return M模块版本控制
在模块中包含版本字段。调用者可以检查版本,以确保兼容性。请使用语义化版本号(major.minor.patch)。
-- mylib.lua
local M = {}
M._VERSION = "1.2.3"
M._NAME = "mylib"
M._DESCRIPTION = "My Lua library"
function M.hello(name)
return "Hello, " .. (name or "World") .. "!"
end
return M
-- Usage:
local mylib = require("mylib")
print(mylib._VERSION) -- 1.2.3
print(mylib.hello("Lua"))子模块
大型库通常会拆分为多个子模块。主模块可以 require 并重新导出子模块,也可以让每个子模块独立使用。请将文件组织在与模块路径相匹配的目录中。
-- mylib/init.lua (loaded by require("mylib"))
local M = {}
M.strings = require("mylib.strings")
M.tables = require("mylib.tables")
M.math = require("mylib.math")
M._VERSION = "2.0.0"
return M
-- Users can require the whole library:
-- local mylib = require("mylib")
-- mylib.strings.trim(...)
-- Or individual sub-modules:
-- local strs = require("mylib.strings")带元表的模块
为模块设置包含 __call 的元表,即可使模块可调用。这适用于主要功能是函数、同时还带有实用工具的模块——“主要”操作是调用,实用工具则是字段。
-- format.lua
local M = {}
setmetatable(M, {__call = function(_, fmt, ...)
return string.format(fmt, ...)
end})
function M.pad(s, width, char)
char = char or " "
return string.rep(char, math.max(0, width - #s)) .. s
end
return M
-- Usage:
-- local fmt = require("format")
-- print(fmt("%.2f", 3.14)) -- 3.14
-- print(fmt.pad("42", 5)) -- 42延迟加载子模块
使用 __index 延迟加载子模块——仅在首次访问时加载。对于包含许多子模块的大型库,这可以加快启动速度。
-- biglib.lua
local M = {}
local submodules = {"strings", "tables", "math", "io"}
setmetatable(M, {
__index = function(t, k)
for _, name in ipairs(submodules) do
if name == k then
local mod = require("biglib." .. k)
rawset(t, k, mod)
return mod
end
end
return nil
end
})
return M
-- Loads biglib.strings only when accessed:
-- local lib = require("biglib")
-- lib.strings.trim(...)模块测试模式
在模块文件底部添加测试函数或代码块,并设置条件,使其仅在文件被直接运行时执行(而不是被 require 时执行)。这样可以让单元测试与代码放在一起。
-- utils.lua
local M = {}
function M.clamp(v, lo, hi)
return math.max(lo, math.min(hi, v))
end
-- Self-test: only runs when executed directly
if debug.getinfo(2, "S") == nil then
-- Running as main script, not required
print("Testing clamp...")
assert(M.clamp(5, 0, 10) == 5)
assert(M.clamp(-1, 0, 10) == 0)
assert(M.clamp(15, 0, 10) == 10)
print("All tests passed!")
end
return M快速检查
在 Lua 模块中定义私有状态的标准方式是什么?
回顾:编写模块
总结:
- 模式:
local M = {} ... return M - 文件中的局部变量 = 私有;M 字段 = 公共 API
- 模块级代码会在第一次 require 时运行一次
- 通过模块表中的构造函数导出类
- 通过
__index延迟加载子模块 - 包含
_VERSION以检查兼容性
常见问题解答
「编写模块文件」课时是免费的吗?
是的 — 「编写模块文件」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。
「编写模块文件」这节课中我会学到什么?
通过从 .lua 文件返回函数表来创建模块。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Lua Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「编写模块文件」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Lua Academy 课中编写并运行代码吗?
能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。