Lua笔记——4.Package
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._VERSIONendLua5.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,代碼如下:
方法二:在調用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._VERSIONendLua5.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._VERSIONendLua5.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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bootstrap轮播图自动播放响应式箭
- 下一篇: Codeforces937D Sleep