Lua中也拥有和Python世界相似的代码的作用范围和组织方式,下面我们就来简单解析Lua中的全局环境、包、模块组织结构,需要的朋友可以参考下

模块就是一个程序库,而包是一系列模块。Lua中可以通过require来加载模块,然后得到一个全局变量表示一个table。Lua将其所有的全局变量保存在一个被称为“环境”的常规table中。本文首先介绍环境的一些实用技术,然后介绍如何引用模块及编写模块的基本方法。

1. 环境
Lua将环境table保存在一个全局变量_G中,可以对其访问和设置。有时我们作一个全局变量,而它的名称却存储在另一个变量中,或者需要通过运行时的计算才能得到,可以通过value = _G[varname]来获得动态名字的全局变量。

关于“环境”的一大问题是它是全局的,任何对它的修改都会影响程序的所有部分。Lua 5允许每个函数拥有一个子集的环境来查找全局变量,可以通过setfenv来改变一个函数的环境,第一个参数若是1则表示当前函数,2则表示调用当前函数的函数(依次类推),第二个参数是一个新的环境table。

a = 1
setfenv(1, {})
print(a) -- 会报错,print是一个nil。这是因为一旦改变环境,所有的全局访问都会使用新的table

为了避免上述问题,可以使用setfenv(1, {_G = _G})将原来的环境保存起来,然后用_G.print来引用。另一种组装新环境的方法是使用继承,下面的代码新环境从源环境中继承了print和a,任何赋值都发生在新的table中。

a = 1
local newgt = {}
setmetatable(newgt, {__index = _G})
setfenv(1, newgt)
print(a)

2. 模块与包
2.1 调用模块

要调用模块mod中的foo方法,可以用require函数来加载,如:

require "mod"
mod.foo()
-- 或者
local m = require "mod"
m.foo()

require函数的行为: (关于require使用的路径查找策略不赘述)
在package.loaded这个table中检查模块是否已加载
=> 已加载,就返回相应的值(可见一个模块只会加载一次)
=> 未加载,就试着在package.preload中查询传入的模块名
===> 找到一个函数,就以该函数作为模块的加载器
===> 找不到,则尝试从Lua文件或C程序库中加载模块
=====> 找到Lua文件,通过loadfile来加载文件
=====> 找到C程序库,通过loadlib来加载文件

2.2 使用环境

下面的代码说明了如何用环境来创建一个复数(complex)模块:

-- 模块设置
local modname = "complex"
local M = {}
_G[modname] = M
package.loaded[modname] = M

-- 声明模块从外界所需的所有东西
local _G = _G -- 保留旧环境的引用,使用时需要像_G.print这样用
local io = io

-- 运行这句之后环境就变了
setfenv(1, M)

function new(r, i) return {r=r, i=i} end

function add(c1, c2)
  return new(c1.r + c2.r, c1.i + c2.i)
end

这样声明函数add时,就成为了complex.add,调用同一模块的其他函数也不需要加前缀。

2.3 module函数

Lua 5.1提供了一个新函数module,囊括了上面一系列定义环境的功能。在开始编写一个模块时,可以直接用module("modname", package.seeall)来取代前面的设置代码。在一个模块文件开头有这句调用后,后续所有代码都不需要限定模块名和外部名字,同样也不需要返回模块table了。

2.4 子模块与包

Lua支持具有层级的模块名,用一个点来分隔名称中的层级。例如一个模块名为mod.sub,就是mod的一个子模块。一个包(package)就是一个完整的模块树,它是Lua中发型的单位。注意,当搜索一个子模块文件时,require会把点号当做目录分隔符来搜索,也就是说调用require "a.b"会尝试打开./a/b.lua,/usr/local/lua/a/b.lua,/usr/local/lua/a/b/init.lua。通过这种加载策略,可以将包的所有模块组织到一个目录中。

2.5 以自定义方式加载 lua 模块
从 Lua 5.1 以后,Lua 有了标准的模块管理库。所以所有的模块加载都是通过 require 来完成。 require 的设计是颇具扩展性的,它会从若干个定义好的 loader 中逐个尝试加载新的模块。系统库中提供了四个 loader ,分别实现已加载模块,Lua 模块,和 C 扩展模块(用了两个 loader 来实现 C 扩展模块的加载)。这些 loader 以 CFunction 的形式放在 require 的环境中的一个 table 里。

如果我们想改变 lua 模块的加载形式,只需要替换或增加一个新的 loader 就可以了。

要做的只需要模仿 loadlib.c 中的 loader_Lua 函数做一个自己的实现,比如在我们的项目中,就允许从自定义格式数据包中,加载一个被加密过的 Lua 代码文件。然后写几行 C 代码,获得 require 的环境(使用 lua_getfenv ),然后取出其中 "loaders" 这个 table ,把新的自定义 loader 插入到 index 2 的地方。

具体的代码就不详述了,仔细阅读一下 ll_require 的实现(在 loadlib.c 中)就很容易明白。我们的整个工作从分析到实现没有超过两个小时,这真是得益于 Lua 良好的设计啊 :D 甚至如果你想从一个网络连接的数据流中加载 Lua 模块,或是通过 http/ftp 协议下载,也是行的通的吧。

最新资讯
马斯克:特斯拉当前可能被高估 但五年后股价会更高

马斯克:特斯拉当前可能

埃隆·马斯克在接受采访时表示,他认为特斯拉“股价在五
银保监会规范互联网保险 非持牌机构不得开展互联网保险业务

银保监会规范互联网保

为有效防范化解风险,保护消费者权益,推动互联网保险业务
嫌苹果收费太高 国外厂商们开始组队反抗了

嫌苹果收费太高 国外

近年发展势头迅猛的游戏公司Epic和苹果以及谷歌干起来
美团“无边界”式扩张:进军美妆和宠物业 抢食电商巨头?

美团“无边界”式扩张

可以预见,未来的零售业,还远未形成定局。无论是电商、O2
蚂蚁战配基金点燃银行 平台销售“踩线”行为屡禁不止

蚂蚁战配基金点燃银行

9月25日开售的5只可战略配售蚂蚁集团股票的新基金,仍在
挖掘电竞线下消费新场景 首个电竞体验馆落地上海

挖掘电竞线下消费新场

作为电竞重要的承载单位,电竞体验馆起到了将线上用户向
最新文章
如何使用Vim搭建Lua开发环境详解

如何使用Vim搭建Lua开

这篇文章主要给大家介绍了关于如何使用Vim搭建Lua开发
Lua中pairs与ipairs的区别总结

Lua中pairs与ipairs的

这篇文章主要给大家介绍了关于Lua中pairs与ipairs区别
Lua语言新手简单入门教程

Lua语言新手简单入门

这篇文章主要给大家介绍的是关于Lua语言新手入门的简
利用Lua定制Redis命令的方法详解

利用Lua定制Redis命令

这篇文章主要给大家介绍了关于利用Lua定制Redis命令的
深入解读Lua中迭代器与泛型for的使用

深入解读Lua中迭代器

在Lua中,迭代器常被写为函数而被调用返回下一个元素,
Lua中函数与面向对象编程的基础知识整理

Lua中函数与面向对象

函数在面对对象的编程中又被叫做方法,会受到作用域的