module简介:

Lua 5.1 加入模块管理机制module,类似于Java的packages、C++的namespaces,可以通过require用于加载模块,module用于创建模块。require加载一个自定义或者第三方的module,然后便得到了一个全局变量,表示一个table。

Lua 5.2 之后则去掉了module创建模块的函数,仅保留requir加载函数在全局环境

require函数

创建模块

在Lua中创建一个模块最简单的方法:创建一个table,并将所有需要导出的函数放入其中,最后返回这个table,相当于将导出的函数作为table的一个字段。

创建一个简单模块,代码如下:

    --file: simpleMod.lualocal _Mod = {}_Mod._Mod = _Modfunction _Mod:New(name)local mod = {}mod.Name = name or "Default Name"mod._VERSION = 0.01return setmetatable(mod , {__index = self})endfunction _Mod:Update()self._VERSION = self._VERSION + 0.01return self._VERSIONendreturn _Mod

调用模块

require (modname)

Loads the given module. The function starts by looking into the package.loaded table to determine whether modname is already loaded. If it is, then require returns the value stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.

To find a loader, require is guided by the package.loaders array.(package.loaders array:A table used by require to control how to load modules.) By changing this array, we can change how require looks for a module. The following explanation is based on the default configuration for package.loaders.

First require queries package.preload[modname].(A table to store loaders for specific modules) If it has a value, this value (which should be a function) is the loader. Otherwise require searches for a Lua loader using the path stored in package.path. If that also fails, it searches for a C loader using the path stored in package.cpath. If that also fails, it tries an all-in-one loader (see package.loaders).

Once a loader is found, require calls the loader with a single argument, modname. If the loader returns any value, require assigns the returned value to package.loaded[modname]. If the loader returns no value and has not assigned any value to package.loaded[modname], then require assigns true to this entry. In any case, require returns the final value of package.loaded[modname].

If there is any error loading or running the module, or if it cannot find any loader for the module, then require signals an error.

require函数的调用形式为require "模块名"

加载给定的模块名modname,require函数首先会在表package.loaded中查看是否已经加载过,如果已经加载过,则会返回储存在package.loaded[modname]中的模块,否则,require函数将会尝试为此模块寻找一个加载器。

require函数将会被package.loader的数组引导来寻找适用于该模块的加载器,(package.loaders,是一个被require函数用来控制如何加载模块的表或者说数组)我们可以自己改变require函数寻找模块的方法,下面是package.loaders的默认配置的下的解释:

首先,require函数会查看package.preload[modname](用来储存特定模块加载器的表),如果有值(该值应是一个函数),则改值就是要找的加载器。否则,require函数将会通过储存在package.path中的路径来寻找一个Lua 加载器。如果也失败了,requier函数则会通过储存在package.cpath中的路径来寻找一个C 加载器。如果也失败了,require函数将会尝试使用一个package.loaders中的通用的加载器——all-in-one 加载器。

一旦找到加载器,require函数会传递一个单一的参数模块名modname到这个加载器,如果加载器有任何的返回值,require函数会将返回的值连同模块名modname会注册到表 package.loaded[modname]中。如果加载器没有返回值并且还没有任何值连同模块名modname会注册到表 package.loaded[modname]中,那么require函数将会注册 true 到该键值对的入口。任何情况下,require 函数都会返回表 package.loaded[modname]最终的值。

如果在加载或者运行该模块,亦或者完全没有找到该模块的加载器,则require函数会显示error。

将上方simpleMod.lua放置在当前目录的子目录Util下,调用代码:

    --file: testMod.luaprint("Before the require function , packages in the package.loaded :")for k in pairs(package.loaded) do print(k) endprint("package.preload loader number : "..#package.preload)--lua5.1中的package.loaders 在lua5.2之后版本中更名为package.searchers--所以使用package.loaders or package.searchers来兼容版本for k,v in pairs(package.loaders or package.searchers) do print("loader : "..k .. "   "..tostring(v)) end--require function ,PS: The Util Is A Subdirectory of current directorylocal Mod = require "Util.simpleMod"print("\nAfter the require function , the table package.loaded")for k in pairs(package.loaded) do print(k) end--simpleMod's usage codelocal mod = Mod:New("TestFeature")mod:Update()print("\nModFeature Name : "..mod.Name .. "\nVersion : "..mod._VERSION)

输出结果:

module函数

简化module的创建

  • require会将模块名modname传递给loader,在loader加载模块时,我们可以在模块中接收传递的模块名

  • 有时我们会漏写创建模块最后的return语句,我们可以将所有与模块创建相关的设置任务都集中在开头,
    消除return语句的一种方法是,loader加载模块时将模块名modname以及模块注册至表package.loaded[modname]中

代码如下:

    --file: simpleMod.lualocal _Mod = {}_Mod._Mod = _Modlocal modname = ..._G[modname] = _modpackage.loaded[modname] = _Modfunction _Mod:New(name)local mod = {}mod.Name = name or "Default Name"mod._VERSION = 0.01return setmetatable(mod , {__index = self})endfunction _Mod:Update()self._VERSION = self._VERSION + 0.01return self._VERSIONend

Lua5.1 & setfenv (f, table)

Sets the environment to be used by the given function. f can be a Lua function or a number that specifies the function at that stack level: Level 1 is the function calling setfenv. setfenv returns the given function.

As a special case, when f is 0 setfenv changes the environment of the running thread. In this case, setfenv returns no values.

当我们在创建模块或者访问同一个模块中的其它函数时,需要限定名称,就比如上面代码中的_Mod,为此,我们可以让模块的主程序块有一个独立的环境,这样不仅它的所有函数都可共享这个table,而且它的所有全局变量也都记录在这个table中。而模块所要做的就是将这个table赋予模块名和package.loaded。

这样,我们在调用同一个模块中的函数new时,也不用指定m了。在写自己的模块时,就省去了前缀;但是与此同时,当我们调用setfenv(1,m)函数之后,会将一个空table m作为环境,但是这样之后就无法访问前一个环境中全局变量了(例如setmetatablet之类的全局变量)

解决方法:
方法一:在调用setfenv(1,m)之前,为m设置元表,使元表的__index域指向_G,代码如下:

--file: simpleMod.lualocal _Mod = {}_Mod._Mod = _Modlocal modname = ..._G[modname] = _modpackage.loaded[modname] = _Mod--Before calling the func setfenv() ,set the mestatable {__index = _G} to the _modsetmetatable(_Mod,{__index = _G})setfenv(1,_Mod)function New(self,name)local mod = {}mod.Name = name or "Default Name"mod._VERSION = 0.01return setmetatable(mod , {__index = self})endfunction Update(self)self._VERSION = self._VERSION + 0.01return self._VERSIONend

方法二:在调用setfenv(1,m)之前,使用局部变量将全局变量_G保存起来,代码如下:

--file: simpleMod.lualocal _Mod = {}_Mod._Mod = _Modlocal modname = ..._G[modname] = _modpackage.loaded[modname] = _Mod--Before calling the func setfenv() ,storage _G to the local variablelocal _G = _Gsetfenv(1,_Mod)function New(self,name)local mod = {}mod.Name = name or "Default Name"mod._VERSION = 0.01--This way to use the variables in the _G table return _G.setmetatable(mod , {__index = self})endfunction Update(self)self._VERSION = self._VERSION + 0.01return self._VERSIONend

方法三:在调用setfenv(1,m)之前,只将需要使用的全局变量保存起来,代码如下:

    --file: simpleMod.lualocal _Mod = {}_Mod._Mod = _Modlocal modname = ..._G[modname] = _modpackage.loaded[modname] = _Mod--Before calling the func setfenv() ,storage useful variable the local variablelocal setmetatable = setmetatablesetfenv(1,_Mod)function New(self,name)local mod = {}mod.Name = name or "Default Name"mod._VERSION = 0.01--This way to use the variables in the _G table return setmetatable(mod , {__index = self})endfunction Update(self)self._VERSION = self._VERSION + 0.01return self._VERSIONend

Lua5.1 & module(...)

在Lua 5.1中,可以用module(...)的函数来代替以下代码:

-- local _Mod = {}-- _Mod._Mod = _Mod-- local modname = ...-- _G[modname] = _mod-- package.loaded[modname] = _Mod-- setfenv(1,_Mod)

由于在默认情况下,module不提供外部访问,必须在调用它之前,为需要访问的外部函数或模块声明适当的局部变量。然后Lua提供了一种更为方便的实现方式,即在调用module函数时,多传入一个package.seeall的参数,相当于 setmetatable(_Mod, {__index = _G}):

module(...,package.seeall)

完整代码:

--file: simpleMod.lua-- local _Mod = {}-- _Mod._Mod = _Mod-- local modname = ...-- _G[modname] = _mod-- package.loaded[modname] = _Mod--Before calling the func setfenv() ,set the mestatable {__index = _G} to the _mod-- setmetatable(_Mod,{__index = _G})-- setfenv(1,_Mod)module(...,package.seeall)function New(self,name)local mod = {}mod.Name = name or "Default Name"mod._VERSION = 0.01--This way to use the variables in the _G table return setmetatable(mod , {__index = self})endfunction Update(self)self._VERSION = self._VERSION + 0.01return self._VERSIONend

Lua5.2之后

  • Function module is deprecated. It is easy to set up a module with regular Lua code. Modules are not expected to set global variables.
  • Functions setfenv and getfenv were removed, because of the changes in environments.

  • module函数被抛弃。用普通的Lua代码就可以很容易的创建模块。而模块也不需要去设置全局变量。
  • setfenv以及getfenv函数被移除,因为会对环境产生改变。

Lua5.2之后,如果require引入使用module声明和定义的模块就会报错

REF

http://book.luaer.cn/

http://www.lua.org/manual/5.1/manual.html#5.3

http://www.lua.org/manual/5.2/manual.html#pdf-package.searchers

http://www.jb51.net/article/55818.htm

https://moonbingbing.gitbooks.io/openresty-best-practices/lua/not_use_module.html

https://www.cnblogs.com/zsb517/p/6822870.html

转载于:https://www.cnblogs.com/sylvan/p/8592472.html

Lua笔记——4.Package相关推荐

  1. Lua笔记-关于lua table的C API

    //Lua笔记-关于lua table的C API //转载请注明来自yuliying的CSDN博客. //Lua版本5.2 /*相关API: ====lua_createtable 原型: void ...

  2. lua笔记之local

    lua笔记之local 最近使用lua语言遇到一个小问题,这里要记录一下 lua 中使用 local 的变量,例如: function testLocal()print(a, b)a = 10086l ...

  3. 脚本语言lua笔记(5)c++调用lua

    首先搭建环境,使用vs2010的c++开发工具,lua源码包,可以去官方下载最新源码包,我采用的是lua-5.1.5的版本.好了,开始配环境. 第一步: 下载源码包后,解压lua-5.1.5源码包到硬 ...

  4. Lua笔记6 编译、执行与错误

    1. 编译 dofile() 运行时编译: loadfile() loadstring(string)() 返回一个函数,并执行 例: loadstring("print('hello wo ...

  5. 1.7(java学习笔记)package和import

    package package主要用于管理类,在java中同一个包下不能有相同的类名,可有时项目总会出现很多同名的类,这时就需要通过包来管理类.不同的包下可以有相同的类名. 包就有点类似于文件夹,不同 ...

  6. Lua笔记4 闭包、迭代器

    闭包 首先,明确闭包的概念.个人理解,一个闭包是一个封闭的数据和状态集合,闭包在创建的时候,可以捕获包外的数据,然后在包内会有一个数据副本,闭包内数据的生命周期随着闭包的结束而结束.在lua中,闭包是 ...

  7. Lua笔记3 函数和数组

    函数 这里主要介绍可变参数函数,使用...表示可变参数函数. function add( ... )local s = 0for i, v in ipairs{...} do -- {...}表示可变 ...

  8. Lua笔记2 变量、循环和流程控制

    变量 变量使用前必须声明,lua的变量默认全局的,哪怕该变量在函数中.局部变量使用local进行声明,仅在作用域中有效.函数中声明的变量,在函数调用前是不会起作用的. 代码示例: function f ...

  9. lua笔记1 基础概念

    基础概念 Lua的变量默认是全局的,如果想要清除,只需要设置为nil.没有赋值的变量也是nil.注意lua是动态语言,变量不需要声明类型,而且自动类型转换. b = 1 print(b) b = ni ...

最新文章

  1. XML与Webservices相关的安全问题概述
  2. Visual Studio 2019设置回车代码补全
  3. 使用bbscope进行大规模域名收集扫描
  4. 常见问题:为什么我的插件安装失败?
  5. HDU-6441-Find Integer-费马大定理+奇偶数列法则
  6. 个人网站搭建,个人网站需要什么软件
  7. 5.42如何高效的学习开源项目
  8. 百度图片推广怎么做(百度图片推广操作方法)
  9. “耳根”发布新作《一念永恒》,交易记录
  10. 前端利用docker在linux上部署nginx服务
  11. Flex Programming Tricks 1
  12. 戴尔inspiron14 5000系列拆机图解教程
  13. Toy3D引擎:网页游戏3D化的利器
  14. unity敌人跟随(导航)
  15. 计算机电子信息专业代码,电子信息科学与技术专业代码:080714T[本科]
  16. java——创意图形项目总结
  17. 音频剪辑软件QLab Pro for Mac
  18. DP4344兼容CS4344-DA转换器
  19. web前端开发培训,261页前端面试题宝典
  20. matlab 求单位样值响应,单位样值响应

热门文章

  1. 作幼儿教育软件的感受(2005-05-09)
  2. php显示TABLE数据
  3. 详解RMQ LCA
  4. 大型网站架构模式之一
  5. UML几个关系图表示
  6. 动态规划之划分数组形成两个和相等的子集
  7. 一个完整的DS1302时钟在STM32上的应用实例
  8. 格式化大容量硬盘为fat32
  9. SCOM 2012知识分享-26:分布式部署要点总结
  10. 开机后将sim/uim卡上的联系人写入数据库