Lua 模塊與包

Lua 模塊與包

摘要

  1. 模塊的概念
  2. 如何實現一個模塊
  3. 如何引用一個模塊
  4. 模塊加載路徑 package.path
  5. 環境變量 LUA_PATH 的設置
  6. 跨目錄下的模塊引用
  7. 緩存機制
  8. 執行環境
  9. 參考

Lua 中模塊的概念

  • 模塊相似於一個封裝庫,從 Lua 5.1 開始,Lua 加入了標準的模塊管理機制,能夠把一些公用的代碼放在一個文件裏,以 API 接口的形式在其餘地方調用,有利於代碼的重用和下降代碼耦合度。
  • Lua 的模塊是由變量、函數等已知元素組成的 table,所以建立一個模塊很簡單,就是建立一個 table,而後把須要導出的常量、函數放入其中,最後返回這個 table 就行。

如何實現一個模塊

-- 初始化一個對象
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

參考

  1. http://www.runoob.com/lua/lua...
  2. 【Nginx Lua 開發實戰】

相關文章
相關標籤/搜索