15.元表与元方法

元表,即 Lua 中普通 table 的元数据表,而元方法则是元表中定义的普通表的默认行为。
Lua 中的每个普通 table 都可为其定义一个元表,用于扩展该普通 table 的行为功能。例如,
对于 table 与数值相加的行为,Lua 中是没有定义的,但用户可通过为其指定元表来扩展这
种行为;再如,用户访问不存在的 table 元素,Lua 默认返回的是 nil,但用户可能并不知道
发生了什么。此时可以通过为该 table 指定元表来扩展该行为:给用户提示信息,并返回用
户指定的值。

(1) 重要函数
元表中有两个重要函数:
 setmetatable(table,metatable):将 metatable 指定为普通表 table 的元表。
 getmetatable(table):获取指定普通表 table 的元表。

(2) _ index 元方法
当用户在对 table 进行读取访问时,如果访问的数组索引或 key 不存在,那么系统就会
自动调用元表的
index 元方法。该重写的方法可以是一个函数,也可以是另一个表。如果
重写的
_index 元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回 nil。
__index元方法是一个函数如下:

--元表与元方法
t1 = {"a", age = 23,"b","c",name = "王五", "d",100}
--定义一个元表
meta = {}
--关联原始表和元表
setmetatable(t1,meta)

function meta.__index(tab,key)
	print("key "..key.." 在原始表中不存在")
end

print(t1[99])
print(t1[100])

//输出结果如下:
key 99 在原始表中不存在
nil
key 100 在原始表中不存在
nil

__index元方法是另一个表如下

t1 = {"a", age = 23,"b","c",name = "王五", "d",100}
--定义一个元表
meta = {}

--定义一个元表
meta = {}

--定义另一个普通表
other = {}
other[99] = 999
other[100] = 1000
--关联原始表和元表
setmetatable(t1,meta)
meta.__index = other

print(t1[99])
print(t1[100])
//输出结果如下:
999
1000

3.当用户为 table 中一个不存在的索引或 key 赋值时,就会自动调用元表的_ newindex 元
方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的
_newindex 元方法
是函数,且有返回值,则直接返回;如果没有返回值,则返回 nil。
__newindex是函数,如下:

t1 = {"a", age = 23,"b","c",name = "王五", "d",100}
--元表
meta = {};

setmetatable(t1,meta)

meta.__newindex = function(tab,key,value)
	print("写 "..key.." value不存在")
	--新值写入原始表
	rawset(tab,key,value)
end

t1[100] = 1900
t1.x = "xxx"

print (t1[100])
print (t1.x)
//输出结果如下:
写 100 value不存在
写 x value不存在
1900
xxx

__newindex是另一个表,如下:

t1 = {"a", age = 23,"b","c",name = "王五", "d",100}
meat = {}
other = {}

setmetatable(t1,meat)

meat.__newindex = other --此时,other的作用是,暂存新增加的数据

t1.x = "xxx369"

print(t1.x)
print(other.x)
//输出结果如下:
nil
xxx369

4.运算符元方法
如果要为一个表扩展加号(+)、减号(-)、等于(==)、小于(<)等运算功能,则可重写相应的
元方法。
例如,如果要为一个 table 扩展加号(+)运算功能,则可重写该 table 元表的_ add 元方
法,而具体的运算规则,则是定义在该重写的元方法中的。这样,当一个 table 在进行加法
(+)运算时,就会自动调用其元表的
_add 元方法。

t1 = {"a", age = 23,"b","c",name = "王五", "d",100}


meta = {
__add = function(tab,num)
for k,v in pairs(tab) do
	if (type(v) == "number") then

		tab[k] = v + num

	elseif (type(v) == "string" and type(k) ~= "string") then

		tab[k] = tab[k]..".xxx"

	end
end
	return tab
end
}

setmetatable(t1,meta)

t2 = t1 + 3

for k,v in pairs(t2) do
	print(k,v)
end

//输出结果如下:
1	a.xxx
2	b.xxx
3	c.xxx
4	d.xxx
5	103
age	26
name	王五

5._tostring元方法
直接输出一个 table,其输出的内容为类型与 table 的存放地址。如果想让其输出 table
中的内容,可重写
_tostring 元方法。

  t1 = {"a", age = 23,"b","c",name = "王五", "d",100}


meta = {}

setmetatable(t1,meta)

meta.__tostring = function (tab)
	str = ""
	for k, v in pairs(tab) do
		str = str..k..": "..v.."\n"
	end
	return str
end

print(t1)
//输出结果如下:
1: a
2: b
3: c
4: d
5: 100
age: 23
name: 王五

6._call元方法
当将一个 table 以函数形式来使用时,系统会自动调用重写的
_call 元方法。该用法主
要是可以简化对 table 的相关操作,将对 table 的操作与函数直接相结合。

t1 = {"a", age = 23,"b","c",name = "王五", "d",100}

meta = {}

setmetatable(t1,meta)

function meta.__call(tab,num,str)
	for k,v in pairs(tab) do
		if (type(v) == "string") then
			tab[k] = tab[k]..str
		else
			tab[k] = v + num
		end
	end
	return tab
end

t1(1,"xx")

for k,v in pairs(t1) do
	print(k, v)
end
//输出结果如下:
1	axx
2	bxx
3	cxx
4	dxx
5	101
age	24
name	王五xx

7.元表单独定义
为了便于管理与复用,可以将元素单独定义为一个文件。该文件中仅可定义一个元表,
且一般文件名与元表名称相同。
若一个文件要使用其它文件中定义的元表,只需使用 require “元表文件名”即可将元表
导入使用。
如果用户想扩展该元表而又不想修改元表文件,则可在用户自己文件中重写其相应功能
的元方法即可。

posted @ 2024-04-13 20:03  test369  阅读(13)  评论(0编辑  收藏  举报