Lua的模塊與包機制


從用戶的觀點來看,一個模塊就是一個程序庫,可以通過require來加載。然后得到了一個全局變量,表示一個table。

這個table就像一個名稱空間,其內容就是模塊中導出的所有東西,比如函數和變量。一個規范的模塊還應該使得require返回這個table。

顯然,在Lua中,模塊也是"第一類值"。

 

比如,用戶需要調用一個模塊中的函數,最簡單的方法:

require "mod"

mod.foo

 

如果希望能使用較短的模塊名稱,則可以為模塊設置一個全局名稱:

local m = require "mod"

m.foo()

 

還可以為個別函數設置不同的名稱:

require "mod"

local f = mod.foo

f()

 

require 函數細節

require函數加載模塊時,會檢查package.loaded是否已經加載。所以一個模塊只會加載一次。

如果require為指定模塊找到了一個Lua文件,它就會通過loadfile來加載該文件。如果找到的是一個C程序庫,就通過loadlib來加載。請注意loadfile和loadlib都只是加載了代碼,並沒有運行它們。為了運行代碼,require會以模塊名作為參數來調用這些代碼。

require用於搜索Lua文件的路徑放在常量package.path中。當Lua啟動后,便以環境變量LUA_PATH的值來初始化這個變量。如果沒有找到這個環境變量,則使用一個編譯時定義的默認路徑來初始化。

 

如果require無法找到和模塊相符的Lua文件,就會尋找C程序庫。這類搜索是從變量package.cpath來獲取路徑。

 

模塊的創建方法

  在Lua中創建一個模塊的最簡單方法是:創建一個table,並將所有需要導出的函數放入其中,最后返回這個table。

比如:

 1 complex = {}
 2 
 3 function complex.new(r, i) return {r=r, i=i} end
 4 
 5 -- 定義一常量
 6 complex.i = complex.new(0,1)
 7 
 8 function complex.add(c1,c2)
 9     return complex.new(c1.r + c2.r, c1.i + c2.i)
10 end
11 
12 function complex.sub(c1, c2)
13     return complex.new(c1.r - c2.r, c1.i - c2.i)
14 end
15 
16 ....
17 
18 return complex

在這里,必須顯示地將模塊名放到每個函數的定義中。而且在函數定義中,一個函數調用另外一個函數,必須限定使用被調用函數的名稱。

  但是,我們是沒有必要寫模塊名字的,因為require會將模塊名字作為參數傳遞給模塊。

 1 local modname = ...
 2 local M = {}
 3 _G[modname] = M
 4 package.loaded[modname] = M
 5 
 6 M.i = {r = 0, i = 1}
 7 
 8 function M.new(r, i)
 9     return {r=r, i=i}
10 end
11 
12 function M.add(c1, c2)
13     return M.new(c1.r + c2.r, c1.i + c2.i)
14 end
15 
16 ...

另外一個改進是不需要return 模塊名字了。因為,如果一個模塊沒有返回值的話,require就會返回package.loaded[modname]的當前值。

 

  現在的創建模塊的基本方法缺點在於:在訪問同一模塊的其他方法時候,必須限定名稱。通過使用"函數環境"這種技術,可以解決這個問題。

 1 local modname = ...
 2 local M = {}
 3 _G[modname] = M
 4 package.loaded[modname] = M
 5 setfenv(1, M)
 6 
 7 function M.new(r, i) return {r=r, i=i} end
 8 
 9 function add(c1, c2)
10     return new(c1.r + c2.r, c1.i + c2.i)
11 end
12 
13 ...

這里add函數就從環境中得到new也就是complex.new。但是由於使用了環境,會導致訪問其他模塊出現問題。比如訪問全局模塊_G。

 

  一般較好的解決辦法是將需要用到的模塊聲明為局部變量:

  

 1 local modname = ...
 2 local M = {}
 3 _G[modname] = M
 4 package.loaded[modname] = M
 5 
 6 -- 導入
 7 local sqrt = math.sqrt
 8 local io = io
 9 
10 setfenv(1, M)

 

  在Lua5.1后,Lua提供了一個新函數modle,包括了:

 

1 local modname = ...
2 
3 local M = {}
4 
5 _G[modname] = M
6 
7 package.loaded[modname] = M
8 
9 setfenv(1, M)

這些功能。默認情況下,module不提供對外訪問。必須在調用它之前,為所需要訪問的外部函數或者模塊聲明恰當的局部變量。也可以通過繼承來實現外部訪問。只需要在module時候加一個選項package.seeall。這個選項等價於以下代碼:

setmetatable(M, {__index = _G}),只需要這么做:module(..., package.seeall)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM