package.path
LUA_PATH
的設置-- 初始化一個對象 local Account = {balance = 0} -- 對外開放 withdraw 函數 function Account.withDraw(v) Account.balance = Account.balance - v end -- 不對外開放 function getBalance() return Account.balance end return Account
新建 Account.lua
文件,如上示例實現了一個名爲 Account
的模塊,經過 return
關鍵字實現內容的導出,其中外部可訪問的內容爲 Account.blance
字段以及 Account.withDraw
函數。html
在 lua 中經過全局函數 require
來實現對其餘模塊的引用。緩存
使用方式:bash
require("<模塊名>")
require "<模塊名>"
示例:函數
#!/usr/local/bin/lua -- 加載 Account.lua 文件 local Account = require("Account") print(Account) -- [[ 遍歷內容 ]] function printTable(_tab) for k, v in pairs(_tab) do print(k, v) end end printTable(Account)
執行結果以下:ui
tangzixiang$ ./Account_test.lua table: 0x7ffc6b406a20 balance 0 withDraw function: 0x7ffc6b407190
從執行結果能夠看出導入的模塊實際即是一個 table 的實現。導入的 Account
模塊包含 balance
字段以及 withDraw
方法。lua
網上不少教程都只是講到這裏,實際上忽略了一個很重要的問題即是不一樣路徑下的模塊的引用。spa
package.path
在 Lua 中你沒法像其餘語言那樣直接經過相對路徑或絕對路徑來引用模塊,Lua 的模塊引用與其加載機制有關,具體加載路徑能夠經過全局對象 package
對象的package.path
字段獲取默認的加載路徑:code
#!/usr/local/bin/lua print(package.path)
執行結果:htm
tangzixiang$ ./package_path.lua /usr/local/share/lua/5.3/?.lua;/usr/local/share/lua/5.3/?/init.lua;/usr/local/lib/lua/5.3/?.lua;/usr/local/lib/lua/5.3/?/init.lua;./?.lua;./?/init.lua
上述示例的 ?
號即表明咱們在 require
函數中的模塊名,如前面的示例 Account
。對象
示例引用一個不存在的模塊:
#!/usr/local/bin/lua require("XXX")
執行結果:
tangzixiang$ ./package_path.lua /usr/local/bin/lua: ./package_path.lua:3: module 'XXX' not found: no field package.preload['XXX'] no file '/usr/local/share/lua/5.3/XXX.lua' no file '/usr/local/share/lua/5.3/XXX/init.lua' no file '/usr/local/lib/lua/5.3/XXX.lua' no file '/usr/local/lib/lua/5.3/XXX/init.lua' no file './XXX.lua' no file './XXX/init.lua' no file '/usr/local/lib/lua/5.3/XXX.so' no file '/usr/local/lib/lua/5.3/loadall.so' no file './XXX.so' stack traceback: [C]: in function 'require' ./package_path.lua:5: in main chunk [C]: in ?
咱們經過 require
的實際加載狀況發現其對 XXX
的查找路徑與前面輸出的 package.path
一致。最後若是找不到則去找 so 文件( C 程序庫)。
require
用於搜索 Lua 文件的路徑是存放在全局變量 package.path
中,當 Lua 啓動後,會以環境變量 LUA_PATH
的值來初始這個環境變量。若是沒有找到該環境變量,則使用一個編譯時定義的默認路徑來初始化,咱們前面看到的即是默認路徑。
到這裏咱們能夠就知道了爲何前面咱們能夠成功經過 require("Account")
加載 Account.lua
,由於默認的 package.path
中包含了 ./?.lua;
,表示會在當前同目錄下尋找指定模塊。
LUA_PATH
的設置若是沒有 LUA_PATH
這個環境變量,也能夠自定義設置。
假設咱們如今有個項目庫叫 lua
,放在了根目錄下,爲了平時能夠更方便的引用,咱們能夠更新 LUA_PATH
讓其包含該項目。 在當前用戶根目錄下打開 .profile
文件,並追加:
#LUA_PATH export LUA_PATH="~/lua/?.lua;;"
文件路徑以 ";" 號分隔,最後的 2 個 ";;" 表示新加的路徑後面加上原來的默認路徑。
接着,更新環境變量參數,使之當即生效。
source ~/.profile
咱們這時再來看下 package.path
的輸出:
tangzixiang$ ./package_path.lua ~/lua/?.lua;/usr/local/share/lua/5.3/?.lua;/usr/local/share/lua/5.3/?/init.lua;/usr/local/lib/lua/5.3/?.lua;/usr/local/lib/lua/5.3/?/init.lua;./?.lua;./?/init.lua;
能夠看到此時的 package.path
已經包含了咱們須要的庫路徑。
注:若是 .profile
不生效,則能夠在嘗試 .bash_profile
或則 .bashrc
文件重複上述操做。
一般咱們在實際開發是都會在工做目錄下經過不一樣的目錄來對功能模塊進行劃分,這時便須要動態的更改加載路徑。
假設有以下路徑:
tangzixiang$ tree . ├── package_path.lua ├── model │ ├── Account.lua └── test └── Account_test.lua 2 directories, 3 files
咱們須要在 test 目錄下執行 Account_test.lua
文件,其中依賴於 model 目錄的 Account.lua
文件
#!/usr/local/bin/lua print("test/Account_test.lua\n") -- 加載 Account.lua 文件 local Account = require "Account" print(Account) function printTable(_tab) for k, v in pairs(_tab) do print(k, v) end end printTable(Account)
執行結果:
tangzixiang$ ./Account_test.lua test/Account_test.lua /usr/local/bin/lua: ./Account_test.lua:7: module 'Account' not found: no field package.preload['Account'] no file '~/lua/Account.lua' no file '/usr/local/share/lua/5.3/Account.lua' no file '/usr/local/share/lua/5.3/Account/init.lua' no file '/usr/local/lib/lua/5.3/Account.lua' no file '/usr/local/lib/lua/5.3/Account/init.lua' no file './Account.lua' no file './Account/init.lua' no file '/usr/local/lib/lua/5.3/Account.so' no file '/usr/local/lib/lua/5.3/loadall.so' no file './Account.so' stack traceback: [C]: in function 'require' ./Account_test.lua:7: in main chunk [C]: in ?
不出意外的發現異常了,由於咱們引用的 Account
不在任何已有的加載路徑下。若是想要可以正確的動態引用咱們須要的模塊,則須要在實際引用前動態的更新 package.path
:
#!/usr/local/bin/lua print("test/Account_test.lua\n") -- 更新 package.path package.path = ";../model/?.lua;" .. package.path local Account = require "Account" print(Account) function printTable(_tab) for k, v in pairs(_tab) do print(k, v) end end printTable(Account)
執行結果:
tangzixiang$ ./Account_test.lua test/Account_test.lua table: 0x7f93f9d00830 balance 0 withDraw function: 0x7f93f9d00150
效果完美。
注意:因爲 package.path
是全局的故一旦更新則會在當前項目內生效。
Lua 也相似其餘大部分語言的模塊導入機制,存在緩存機制,模塊一旦導入有且只在第一次導入時執行一次模塊內容。
A.lua
local Account = require("Account") print(Account)
B.lua:
#!/usr/local/bin/lua package.path = ";./model/?.lua;" .. package.path require("A") local Account = require("Account") print(Account)
執行結果:
tangzixiang$ ./B.lua table: 0x7fabd8600880 table: 0x7fabd8600880
能夠看出 Account
對象只實例化了一次。
tangzixiang:~ tangzixiang$ lua -v Lua 5.3.4 Copyright (C) 1994-2017 Lua.org, PUC-Rio