目录
码云代码链接
https://gitee.com/wenwenc9/lua_pro.git
一、require
模块类似于一个封装库,从 Lua 5.1
开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:
module.lua
module = {}
module.constant = '这是一个常量'
function module.func1()
io.write('这是一个公有函数')
end
local function func2()
io.write('这是一个私有函数')
end
function module.func3()
func2()
end
return module
一、require函数
- require 会搜索目录加载文件
- require 会判断是否文件已经加载避免重复加载同一文件
由于上述特征,require在 Lua 中是加载库的更好的函数。
require 使用的路径和普通我们看到的路径还有些区别,我们一般见到的路径都是一
个目录列表。require 的路径是一个模式列表,每一个模式指明一种由虚文件名(require
的参数)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号的文件
名。匹配的时候 Lua 会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如
果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
调用 require "lili"时会试着打开这些文件:
lili
lili.lua
c:\windows\lili
/usr/local/lua/lili/lili.lua
require 关注的问题只有分号(模式之间的分隔符)和问号,其他的信息(目录分隔
符,文件扩展名)在路径中定义。
为了确定路径,Lua 首先检查全局变量 LUA_PATH 是否为一个字符串,如果是则认
为这个串就是路径;否则 require 检查环境变量 LUA_PATH 的值,如果两个都失败 require使用固定的路径(典型的"?;?.lua")
require
的另一个功能是避免重复加载同一个文件两次。Lua 保留一张所有已经加载
的文件的列表(使用 table 保存)。如果一个加载的文件在表中存在 require 简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名 require同一个文件两次,将会加载两次该文件。比如 require "foo"和 require "foo.lua"
,路径为"?;?.lua"将会加载 foo.lua 两次。我们也可以通过全局变量_LOADED 访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让 require 加载一个文件两次。比如,require "foo"之后_LOADED[“foo”]将不为 nil,我们可以将其赋值为 nil,require "foo.lua"将会再次加载该文件。
一个路径中的模式也可以不包含问号而只是一个固定的路径,比如:
?;?.lua;/usr/local/default.lua
这种情况下,require 没有匹配的时候就会使用这个固定的文件(当然这个固定的路
径必须放在模式列表的最后才有意义)。在 require 运行一个 chunk 以前,它定义了一个全局变量_REQUIREDNAME 用来保存被 required 的虚文件的文件名。我们可以通过使用这个技巧扩展 require 的功能。举个极端的例子,我们可以把路径设为"/usr/local/lua/newrequire.lua"
,这样以后每次调用 require 都会运行 newrequire.lua
,这种情况下可以通过使用_REQUIREDNAME 的值去实际加载 required 的文件。
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。例如:
require("<模块名>")
或者
require "<模块名>"
执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")
print(module.constant)
module.func3()
执行结果
这是一个常量
这是一个私有函数!
可以给加载的模块定义一个变量名,方便调用
二、加载机制
package.path = package.path..";D:/lua-wenwenc9/lua_pro/ws3c/12_模块与包/?.lua"
对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path
中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 “~/lua/” 路径加入 LUA_PATH 环境变量里:
#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"
文件路径以 “;” 号分隔,最后的 2 个 “;;” 表示新加的路径后面加上原来的默认路径。
接着,更新环境变量参数,使之立即生效。
source ~/.profile
这时假设 package.path 的值是:
/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua
那么调用 require(“module”) 时就会尝试打开以下文件目录去搜索目标。
/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua
如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。
搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。
搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。
三、C包
Lua和C是很容易结合的
,使用C为Lua写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:
local path = “/usr/local/lua/lib/libluasocket.so”
local f = loadlib(path, “luaopen_socket”)
loadlib函数加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。
如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打开库
一般情况下我们期望二进制的发布库包含一个与前面代码段相似的stub文件,安装二进制库的时候可以随便放在某个目录,只需要修改stub文件对应二进制库的实际路径即可。
将stub文件所在的目录加入到LUA_PATH,这样设定后就可以使用require函数加载C库了。
四、脚本卸载
require('Test')
print(package.loaded['Test'])
require('Test') -- 加载一次不会加载二次
-- 卸载
package.loaded['Test'] = nil
五、大G表
可以打印此脚本中,所有的系统变量,自己定义变量,参数的都在大G表中存储,
面向对象创建的时候会用到
a = {}
b = 2
function fun()
print('hello')
end
-- 打印大G表
for k,v in pairs(_G) do
print(k,v)
end
a table: 00C29018
string table: 00C28F78
xpcall function: 00C272A8
b 2
package table: 00C26760
tostring function: 00C27008
print function: 00C26EA8
os table: 00C28F50
unpack function: 00C272C8
require function: 00C27E00
getfenv function: 00C26D48
setmetatable function: 00C26CE8
next function: 00C26B48
assert function: 00C26E28
tonumber function: 00C26F68
io table: 00C293B0
rawequal function: 00C26C28
collectgarbage function: 00C26B68
arg table: 00C29400
getmetatable function: 00C26DE8
module function: 00C28080
fun function: 00C2B4C0
rawset function: 00C26DC8
math table: 00C29388
debug table: 00C29108
pcall function: 00C26F08
table table: 00C267D8
newproxy function: 00C20520
type function: 00C26F48
coroutine table: 00C26738
_G table: 00C22FB0
select function: 00C26C08
gcinfo function: 00C26E88
pairs function: 00C21348
rawget function: 00C26C48
loadstring function: 00C26DA8
ipairs function: 00C21458
_VERSION Lua 5.1
dofile function: 00C26B88
setfenv function: 00C26C88
load function: 00C26CC8
error function: 00C26D08
loadfile function: 00C26E08