openresty开发系列22--lua的元表
openresty开发系列22--lua的元表
举个例子,在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。
那如何计算两个table的相加操作a+b?
local t1 = {1,2,3}
local t2 = {4,5,6}
local t3 = t1 + t2 ----> {1,2,3,4,5,6}
类似java的一些操作重载
这种类似的需求,lua 提供了元表(Metatable)和元方法,允许我们改变table的行为,每个行为关联了对应的元方法。
1)setmetatable(table,metatable): 对指定table设置元表(metatable),
如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
2)getmetatable(table): 返回对象的元表(metatable)。
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
等价于:
mytable = setmetatable({},{})
返回对象元表:
getmetatable(mytable) -- 返回mymetatable
元方法的命名都是以 __ 两个下划线开头。
一)__index 元方法
对表读取索引一个元方法
这是 metatable 最常用的键。
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)
中的__index元方法。如果__index是一个表,Lua会在表格中查找相应的键。
local t = {} -- 普通表t为空
local other = {foo = 2} -- 元表 中有foo值
setmetatable(t,{__index = other}) -- 把 other 设为 t 的元表__index
print(t.foo); ---输出 2
print(t.bar); ---输出nil
t.foo为什么会输出2,就是因为我们重写了__index索引的重载,lua在执行中如果t中没有foo,
就会在他的元表中__index中去找,__index等于other,就输出2。
----------------
如果我们把t设置一下foo的值为3,在看看结果
local t = {foo = 3 } -- 普通表t为空
local other = {foo = 2} -- 元表 中有foo值
setmetatable(t,{__index = other}) -- 把 other 设为 t 的元表__index
print(t.foo); ---输出 3
print(t.bar); ---输出nil
---------------------------------------------
如果__index赋值为一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。
local t = {key1 = "value1" }
local function metatable(mytable,key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
setmetatable(t,{__index = metatable})
print(t.key1);
print(t.key2);
print(t.key3);
分析:
print(t.key1); ---这个输出value1 ,是因为t表中有此key
print(t.key2); ---这个输出metatablevalue,是因为t表中没有此key,就会调用t的元表中的元方法__index,
---这是__index是个函数,就会执行这个函数,传t表和key值这两个参数到此函数,
---函数体中判断有此key2 就输出metatablevalue;
print(t.key3); ---这个输出nil,是因为t表没有,元方法__index函数中 对key3返回nil值
--------------------------------------
总结:lua对表索引取值的步骤
Lua查找一个表元素时的规则,其实就是如下3个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续步骤2
2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续步骤3。
3.判断元表有没有__index元方法,如果__index方法为nil,则返回nil;
如果__index方法是一个表,则重复1、2、3;
如果__index方法是一个函数,则执行该函数,得到返回值。
二)__newindex 元方法
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表进行索引进行赋值,但此索引不存在;lua会查找是否有元表,有元表就会查找__newindex 元方法是否存在:
如果存在则调用__newindex的值进行执行操作,但不会对原表进行赋值操作。
以下实例演示了 __newindex 元方法的应用:
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print("mytable.key1=",mytable.key1)
mytable.newkey = "新值2"
print("mytable.newkey=",mytable.newkey)
print("mymetatable.newkey=",mymetatable.newkey)
mytable.key1 = "新值1"
print("mytable.key1=",mytable.key1)
print("mymetatable.key1=",mymetatable.key1)
以上实例中表设置了元方法 __newindex
在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而对mytable原表不进行赋值。
而对mytable已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。
----------------------------
如果我们要对原来的table进行赋值,那我们就可以用rawset;;__newindex函数会传三个参数,
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(t,k,v) ---第一个参数为table,第二个参数为key,第三个参数为value
rawset(t,k,v);
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print("mytable.key1=",mytable.key1)
print("mytable.key2=",mytable.key2)
key2原本是不再mytable表中的,通过元方法__newindex中函数使用了rawset,就可以对原table进行赋值。
三)为表添加操作符“+”
我们这里定义“+”这元方法,把它定义为两个table相连
如
t1={1,2,3}
t2={4,5,6}
t1 + t2 相加的结果,我们想得到的是 {1,2,3,4,5,6}
那我们如何写元表?
“+”对应的元方法为__add
local function add(mytable,newtable)
local num = table.maxn(newtable)
for i = 1, num do
table.insert(mytable,newtable[i])
end
return mytable
end
local t1 = {1,2,3}
local t2 = {4,5,6}
setmetatable(t1,{__add = add})
t1 = t1 + t2
for k,v in ipairs(t1) do
print("key=",k," value=",v)
end
这样我们就实现了两个table相加
以下是我们的操作符对应关系
模式 描述
__add 对应的运算符 '+'.
__sub 对应的运算符 '-'.
__mul 对应的运算符 '*'.
__div 对应的运算符 '/'.
__mod 对应的运算符 '%'.
__unm 对应的运算符 '-'.
__concat 对应的运算符 '..'.
__eq 对应的运算符 '=='.
__lt 对应的运算符 '<'.
__le 对应的运算符 '<='.
四)__call元方法
__call元方法在 Lua 调用一个值时调用。以下实例演示了计算两个表中所有值相加的和:
类似的 t();类似函数调用
local function call(mytable,newtable)
local sum = 0
local i
for i = 1, table.maxn(mytable) do
sum = sum + mytable[i]
end
for i = 1, table.maxn(newtable) do
sum = sum + newtable[i]
end
return sum
end
local t1 = {1,2,3}
local t2 = {4,5,6}
setmetatable(t1,{__call = call})
local sum = t1(t2)
print(sum)
五)__tostring 元方法
__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容,把表中所有的元素相加输出:
local t1 = {1,2,3}
setmetatable(t1,{
__tostring = function(mytable)
local sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "all value sum =" .. sum
end
})
print(t1) ----print方法会调用table的tostring元方法
到此我们的元表 和 元方法 就讲完了,这个是需要大家自己动手去测试体验的。要有领悟能力
六)点号与冒号操作符的区别
local str = "abcde"
print("case 1:", str:sub(1, 2))
print("case 2:", str.sub(str, 1, 2))
执行结果
case 1: ab
case 2: ab
冒号操作会带入一个 self 参数,用来代表 自己。而点号操作,只是 内容 的展开。
在函数定义时,使用冒号将默认接收一个 self 参数,而使用点号则需要显式传入 self 参数。
obj = { x = 20 }
function obj:fun1()
print(self.x)
end
等价于
obj = { x = 20 }
function obj.fun1(self)
print(self.x)
end
注意:冒号的操作,只有当变量是类对象时才需要。
openresty开发系列22--lua的元表的更多相关文章
- openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息
openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 为了实现业务系统针对不同地区IP访问,展示包含不同地区信息的业务交互界面.很多情况下系统需要根据用户访问的IP信息 ...
- openresty开发系列39--nginx+lua实现接口签名安全认证
一)需求背景现在app客户端请求后台服务是非常常用的请求方式,在我们写开放api接口时如何保证数据的安全,我们先看看有哪些安全性的问题 请求来源(身份)是否合法?请求参数被篡改?请求的唯一性(不可复制 ...
- openresty开发系列38--通过Lua+Redis 实现动态封禁IP
openresty开发系列38--通过Lua+Redis 实现动态封禁IP 一)需求背景为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的 IP 黑名单.对于黑名单之内的 IP ,拒绝 ...
- openresty开发系列24--openresty中lua的引入及使用
openresty开发系列24--openresty中lua的引入及使用 openresty 引入 lua 一)openresty中nginx引入lua方式 1)xxx_by_lua ---> ...
- openresty开发系列36--openresty执行流程之6日志模块处理阶段
openresty开发系列36--openresty执行流程之6日志模块处理阶段 一)header_filter_by_lua 语法:header_filter_by_lua <lua-scri ...
- openresty开发系列23--lua面向对象
openresty开发系列23--lua面向对象 面向对象编程(Object Oriented Programming,OOP)是一种非常流行的计算机编程架构.java,c++,.net等都支持面向对 ...
- openresty开发系列20--lua的时间操作
openresty开发系列20--lua的时间操作 在 Lua 中,函数 time.date 和 difftime 提供了所有的日期和时间功能.在 OpenResty 的世界里,不推荐使用这里的标准时 ...
- openresty开发系列37--nginx-lua-redis实现访问频率控制
openresty开发系列37--nginx-lua-redis实现访问频率控制 一)需求背景 在高并发场景下为了防止某个访问ip访问的频率过高,有时候会需要控制用户的访问频次在openresty中, ...
- openresty开发系列35--openresty执行流程之5内容content阶段
openresty开发系列35--openresty执行流程之5内容content阶段 content 阶段 ---init阶段---重写赋值---重写rewrite---access content ...
随机推荐
- 基于ATtiny85轻松制作一款智能手表
这是基于ATtiny85系列的简约手表系列中的第三款.该款手表通过在微型64x48 OLED显示屏上绘制模拟的手表来显示时间.它使用独立的晶振控制的低功耗RTC芯片来保持每月几秒钟的时间,并在不显示时 ...
- 大数据之路week07--day06 (Sqoop 在从HDFS中导出到关系型数据库时的一些问题)
问题一: 在上传过程中遇到这种问题: ERROR tool.ExportTool: Encountered IOException running export job: java.io.IOExce ...
- #《你们都是魔鬼吗》第八次团队作业:第五天Alpha冲刺
<你们都是魔鬼吗>第八次团队作业:Alpha冲刺 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 你们都是魔鬼吗 作业学习目标 完成最 ...
- 大数相加和大数相乘以及打印从1到最大的n位数
string add(string a, string b){ int nlength; int diff; if (a.size() > b.size()){ nlength = a.size ...
- quiver()函数
1.quiver函数 一般用于绘制二维矢量场图,函数调用方法如下: 1 quiver(x,y,u,v) 该函数展示了点(x,y)对应的的矢量(u,v).其中,x的长度要求等于u.v的列数,y的长度要求 ...
- 向指定URL发送GET和POST请求
package com.sinosoft.ap.wj.common.util; import java.io.BufferedReader;import java.io.IOException;imp ...
- C++ 类的静态成员
https://www.cnblogs.com/lvxiaoning/p/7598062.html
- FailOver的机制
package util import ( "fmt" "hash/crc32" "math/rand" "sort" ...
- js数值的添加与删除
js中数组元素的添加和删除 js中数组元素常用添加方法是直接添加.push方法以及unshift方法 删除方法则是delete.pop.shift 集修改方法为一身的则是splice 1.添加: (1 ...
- mongodb 副本集的主的选举
primary的选举依赖于各个实例的优先权重,默认权重都是1 复本集的主挑选权重最高的,权重一样的无法控制谁为主 设置各个实例的优先权重,挑选自己想要的实例为主,只有primary可以更改权重配置 c ...