# 元表
在Lua中,元表 _(metatable)_ 的表現行為類似于C++語言中的函數(操作符)重載,例如我們可以重載"__add"元方法 _(metamethod)_ ,來計算兩個Lua數組的并集;或者重載"__index"方法,來定義我們自己的Hash函數。Lua 提供了兩個十分重要的用來處理元表的方法,如下:
- setmetatable(table,metatable):此方法用于為一個表設置元表。
- getmetatable(table):此方法用于獲取表的元表對象。
設置元表的方法很簡單,如下:
~~~
mytable = {}
mymetatable = {}
setmetatable(mytable,mymetatable)
~~~
上面的代碼可以簡寫成如下的一行代碼:
~~~
mytable = setmetatable({},{})
~~~
#### 修改表的操作符行為
通過重載"__add"元方法來計算集合的并集實例:
~~~
set1 = {10, 20, 30}--集合
set2 = {20, 40, 50}--集合
union = function (self, another) --將用于重載__add的函數,注意第一個參數是self
local set = {}
local result = {}
--利用數組來確保集合的互異性
for i, j in pairs(self) do set[j] = true end
for i, j in pairs(another) do set[j] = true end
--加入結果集合
for i, j in pairs(set) do table.insert(result, i) end
return result
end
setmetatable(set1, {__add = union}) --重載set1表的__add元方法
set3 = set1 + set2
for _,j in pairs(set3) do
io.write(j.." ") -->輸出結果30 50 20 40 10
end
~~~
除了加法可以被重載之外,Lua提供的所有操作符都可以被重載:
| 元方法 | 含義 |
|-----|-----|
| "__add" | + 操作 |
| "__sub" | - 操作 其行為類似于 "add" 操作 |
| "__mul" | * 操作 其行為類似于 "add" 操作 |
| "__div" | / 操作 其行為類似于 "add" 操作 |
| "__mod" | % 操作 其行為類似于 "add" 操作 |
| "__pow" | ^ (冪)操作 其行為類似于 "add" 操作 |
| "__unm" | 一元 - 操作 |
| "__concat" | .. (字符串連接)操作 |
| "__len" | # 操作 |
| "__eq" | == 操作 函數 getcomphandler 定義了 Lua 怎樣選擇一個處理器來作比較操作 僅在兩個對象類型相同且有對應操作相同的元方法時才起效 |
| "__lt" | < 操作 |
| "__le" | <= 操作 |
除了操作符之外,如下元方法也可以被重載,下面會依次解釋使用方法:
| 元方法 | 含義 |
|-----|-----|
| "__index" | 取下標操作用于訪問 table[key] |
| "__newindex" | 賦值給指定下標 table[key] = value |
| "__tostring" | 轉換成字符串 |
| "__call" | 當 Lua 調用一個值時調用 |
| "__mode" | 用于弱表(_week table_) |
| "__metatable" | 用于保護metatable不被訪問 |
#### __index元方法
下面的例子中,我們實現了在表中查找鍵不存在時轉而在元表中查找該鍵的功能:
~~~
mytable = setmetatable({key1 = "value1"}, --原始表
{__index = function(self, key) --重載函數
if key == "key2" then
return "metatablevalue"
else
return self[key]
end
end
})
print(mytable.key1,mytable.key2) -->value1 metatablevalue
~~~
關于__index元方法,有很多比較高階的技巧,例如:__index的元方法不需要非是一個函數,他也可以是一個表。
~~~
t = setmetatable({[1] = "hello"}, {__index = {[2] = "world"}})
print(t[1], t[2]) -->hello world
~~~
第一句代碼有點繞,解釋一下:先是把{__index = {}}作為元表,但__index接受一個表,而不是函數,這個表中包含[2] = "world"這個鍵值對。所以當t[2]去在自身的表中找不到時,在__index的表中去尋找,然后找到了[2] = "world"這個鍵值對。
__index元方法還可以實現給表中每一個值賦上默認值;和__newindex元方法聯合監控對表的讀取、修改等比較高階的功能,待讀者自己去開發吧。
#### __tostring元方法
與Java中的toString()函數類似,可以實現自定義的字符串轉換。
~~~
arr = {1, 2, 3, 4}
arr = setmetatable(arr, {__tostring = function (self)
local result = '{'
local sep = ''
for _, i in pairs(self) do
result = result ..sep .. i
sep = ', '
end
result = result .. '}'
return result
end})
print(arr) --> {1, 2, 3, 4}
~~~
#### __call元方法
__call元方法的功能類似于C++中的仿函數,使得普通的表也可以被調用。
~~~
functor = {}
function func1(self, arg)
print ("called from", arg)
end
setmetatable(functor, {__call = func1})
functor("functor") --> called from functor
print(functor) --> table: 0x00076fc8 后面這串數字可能不一樣
~~~
#### __metatable元方法
假如我們想保護我們的對象使其使用者既看不到也不能修改 metatables。我們可以對 metatable 設置了__metatable 的值, getmetatable 將返回這個域的值, 而調用 setmetatable將會出錯:
~~~
Object = setmetatable({}, {__metatable = "You cannot access here"})
print(getmetatable(Object)) --> You cannot access here
setmetatable(Object, {}) --> 引發編譯器報錯
~~~
- 序
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- 控制結構for的使用
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- Lua
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- web服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- c10k編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題