14.元表

14.元表


14.1 知识点

元表的概念

  • 任何表变量都可以作为另一个表变量的元表
  • 任何表变量都可以有自己的元表
  • 一个表的元表可以理解为表的父表 当前表可以理解为元表的子表
  • 当我们子表中进行一些特定操作时
  • 会执行元表中的内容

设置和得到元表

设置元表方法 setmetatable(子表,元表(父表))

myMetaTable1 = {}
myTable1 = {}
setmetatable(myTable1, myMetaTable1)

得到元表方法 getmetatable(子表)

print(getmetatable(myTable1)) --table: 00B91AF0
print(myMetaTable1)           --table: 00B91AF0

元表的__tostring方法 子表当成字符串时用到

  • 当子表要被当做字符串使用时 会默认调用子表的元表中的__tostring方法
  • 比如直接打印输出表时 就会调用元表中的tostring方法
  • 元表的__tostring函数变量可以选择有没有参数
  • 没参数的话直接返回字符串即可
  • 有参数的话参数默认传的是子表当参数
myMetaTable2 = {

    -- 无参函数
    -- __tostring = function()
    -- 	return "韬老狮1"
    -- end

    -- 有参函数
    __tostring = function(sonTable)
        return sonTable.name
    end
}
myTable2 = {
    name = "韬老狮2"
}
setmetatable(myTable2, myMetaTable2)
print(myTable2) --韬老狮2 因为是有参函数
-- 如果元表的__tostring使用的是无参函数变量的话就是韬老狮1 
-- 如果元表的__tostring为空会打印表信息table: 00CBA480

元表的__call方法 子表当成函数时用到

  • 当子表被当做一个函数来使用时 会默认调用子表的元表中的___call方法
  • 如果子表被当做一个函数来使用时但子表的元表中的___call方法没有被实现 会报错
myMetaTable3 = {
    --__call方法的第一个参数 是元表的子表
    __call = function(sonTable, parameter1, parameter2)
        print(sonTable) --把子表当做字符串使用 假如当前元表没有__tostring方法就直接打印表变量
        print(parameter1)
        print(parameter2)
        print("韬老狮好爱你")
    end
}

myTable3 = {
    name = "韬老狮3"
}

setmetatable(myTable3, myMetaTable3)

myTable3("一刀999", "两刀666")
-- table: 00BA2830
-- 一刀999
-- 两刀666
-- 韬老狮好爱你

元表的运算重载方法 子表做运算时用到

  • 当子表和别的表做运算符操作时时 会默认调用子表的元表中的一些运算符方法 相当于运算符重载
  • 运算符方法第一个参数是当前子表 第二个参数是和子表操作的表
  • 注意:子表和别的表使用条件运算符做操作时 子表和别的表要共享同一个元表 即别的表的元表也要设置成当前子表的元表 否则使用条件运算符报错
myMetaTable4 = {

    --运算符+
    __add = function(table1, table2)
        return table1.age + table2.age
    end,

    --运算符-
    __sub = function(table1, table2)
        return table1.age - table2.age
    end,

    --运算符*
    __mul = function(table1, table2)
        return table1.age * table2.age
    end,

    --运算符/
    __div = function(table1, table2)
        return table1.age / table2.age
    end,

    --运算符%
    __mod = function(table1, table2)
        return table1.age % table2.age
    end,

    --运算符^
    __pow = function(table1, table2)
        return table1.age ^ table2.age
    end,

    --运算符==
    __eq = function(table1, table2)
        return table1.age == table2.age
    end,

    --运算符<
    __lt = function(table1, table2)
        return table1.age < table2.age
    end,

    --运算符<=
    __le = function(table1, table2)
        return table1.age <= table2.age
    end,

    --运算符..
    __concat = function(table1, table2)
        return table1.age .. table2.age
    end
}

myTable41 = { age = 100 }

setmetatable(myTable41, myMetaTable4)

myTable42 = { age = 10 }

print(myTable41 + myTable42)          --110
print(myTable41 - myTable42)          --90
print(myTable41 * myTable42)          --1000
print(myTable41 / myTable42)          --19
print(myTable41 % myTable42)          --0
print(myTable41 ^ myTable42)          --1e+020

setmetatable(myTable42, myMetaTable4) --为了使用条件运算符比较 两个表的元表必须是同一个表 否则报错

print(myTable41 == myTable42)         --false
print(myTable41 > myTable42)          --true
print(myTable41 <= myTable42)         --false
print(myTable41 .. myTable42)         --10010

元表的__index表 子表得到某属性时用到

  • 当子表中想得某一个属性 但找不到对应属性时 会到子表的元表中找__index表变量中有没有对应属性
myMetaTable5 = {
    __index = { name = "韬老狮5" }
}

myTable5 = {

}

setmetatable(myTable5, myMetaTable5)

print(myTable5.name) --韬老狮5 myTable5中没有name 去myMetaTable5中找到__index指向的表 得到里面的name的值

myMetaTable5.__index = { name = "韬老狮55" } --也可以在外面赋值元表的__index变量

print(myTable5.name) --韬老狮55  myMetaTable5在外面重新设置了__index指向的表
  • 注意:有个小坑 假如元表的__index表变量指向的是自己 假如直接在元表内部声明会打印nil
  • 所以假如元表的__index指向的是自己建议在外部赋值
myMetaTable6 = {
    name = "韬老狮6",
    __index = myMetaTable6
}

myTable6 = {

}

setmetatable(myTable6, myMetaTable6)

print(myTable6.name)                --nil

myMetaTable6.__index = myMetaTable6 --元表的__index指向的是自己建议在外部赋值

print(myTable6.name)                --韬老狮6
  • 元表中的__index表变量假如没有对应属性 那么会嵌套的去元表的元表的的__index表变量中找对应属性 还可以一起继续一层一层往上找 直到找不到
myMetaTable7Father = {
    name = "韬老狮7",
}

myMetaTable7 = {
}

myTable7 = {

}

setmetatable(myTable7, myMetaTable7)

setmetatable(myMetaTable7, myMetaTable7Father)

myMetaTable7.__index = myMetaTable7 --假如myTable7找不到属性去myMetaTable7的__index表变量找 即myMetaTable7自己

myMetaTable7Father.__index =
    myMetaTable7Father              --假如myMetaTable7还找不到属性去myMetaTable7Father的__index表变量找 即myMetaTable7Father自己
print(myTable7.name)                --韬老狮7
  • 不查询元表中的__index表方法 rawget(子表, “要查询的属性”)
  • 该方法只会在子表中查找 子表没有就没有了 不会去元表查找__index表了

print(myTable7.name) --韬老狮7 元表的__index表有name属性

print(rawget(myTable7, "name")) --nil 子表没有name属性

myTable7.name = "韬老狮777" --子表设置name属性

print(rawget(myTable7, "name")) --韬老狮777 找到了子表的name属性

print(myTable7.name) --韬老狮7  找到了子表的name属性就不去找元表了
  • 元表的 __index 可以是一个函数
  • 如果是函数,它会在子表中找不到属性时被调用,并传入两个参数:子表(触发元方法的表)和访问的键。
myMetaTableIndexFunc = {
    __index = function(table, key)
        if key == "greeting" then
            return "Hello from function!"
        else
            return "Key not found"
        end
    end
}
myTableIndexFunc = {}
setmetatable(myTableIndexFunc, myMetaTableIndexFunc)
print(myTableIndexFunc.greeting) -- Hello from function!
print(myTableIndexFunc.otherKey) -- Key not found

元表的__newIndex表 子表改某一个属性赋值时用到

  • 当子表中想改某一个属性并赋值 但找不到对应属性时 会把这个属性赋值到元表的__newindex所指的表中 不会修改子表
myMetaTable8 = {}

myMetaTable8.__newindex = {}

myTable8 = {}

setmetatable(myTable8, myMetaTable8)

myTable8.name = "韬老狮8" --设置了一个myTable7本来并不存在的属性name

print(myTable8.name) -- nil 因为myTable8的元表myMetaTable8中存在__newindex表变量

print(myMetaTable8.__newindex.name) --韬老狮8 name赋值到元表的__newinde表
  • 不修改元表中的__newIndex表方法 rawset(子表, “要设置的属性”, 属性值)
  • 该方法只会在子表设置属性 子表没有该属性 不会去元表设置到__newIndex表上了
print(myTable8.age)                --nil myTable8现在没有age属性

rawset(myTable8, "age", 22)        --设置age属性到子表

print(myMetaTable8.__newindex.age) --nil 元表的__newindex找不到age属性 没有设置到元表的__newindex表中

print(myTable8.age)                --22 子表有age属性了
  • 元表的 __newindex 也可以是一个函数
  • 如果是函数,它会在子表中赋值属性时被调用,并传入三个参数:子表(触发元方法的表)和访问的键和值。
myMetaTableNewIndexFunc = {
    __newindex = function(table, key, value)
        print("Trying to set " .. key .. " to " .. value)
    end
}
myTableNewIndexFunc = {}
setmetatable(myTableNewIndexFunc, myMetaTableNewIndexFunc)
myTableNewIndexFunc.name = "人间自有韬哥在" -- Trying to set name to 人间自有韬哥在

14.2 知识点代码

print("**********元表************")


print("**********知识点一 元表的概念************")
--任何表变量都可以作为另一个表变量的元表
--任何表变量都可以有自己的元表
--一个表的元表可以理解为表的父表 当前表可以理解为元表的子表
--当我们子表中进行一些特定操作时
--会执行元表中的内容


print("**********知识点二 设置和得到元表************")
--设置元表方法 setmetatable(子表,元表(父表))
myMetaTable1 = {}
myTable1 = {}
setmetatable(myTable1, myMetaTable1)
--得到元表方法 getmetatable(子表)
print(getmetatable(myTable1)) --table: 00B91AF0
print(myMetaTable1)           --table: 00B91AF0

print("**********知识点三 元表的__tostring方法************")
--当子表要被当做字符串使用时 会默认调用子表的元表中的__tostring方法
--比如直接打印输出表时 就会调用元表中的tostring方法
--元表的__tostring函数变量可以选择有没有参数
-- 没参数的话直接返回字符串即可
-- 有参数的话参数默认传的是子表当参数
myMetaTable2 = {
    -- __tostring = function()
    -- 	return "韬老狮1"
    -- end
    __tostring = function(sonTable)
        return sonTable.name
    end
}
myTable2 = {
    name = "韬老狮2"
}
setmetatable(myTable2, myMetaTable2)
print(myTable2) --韬老狮2
-- 如果元表的__tostring使用的是无参函数变量的话就是韬老狮1 如果元表的__tostring为空会打印表信息table: 00CBA480


print("**********知识点四 元表的__call方法************")
--当子表被当做一个函数来使用时 会默认调用子表的元表中的___call方法
--如果子表被当做一个函数来使用时但子表的元表中的___call方法没有被实现 会报错
myMetaTable3 = {
    --__call方法的第一个参数 是元表的子表
    __call = function(sonTable, parameter1, parameter2)
        print(sonTable) --把子表当做字符串使用 假如当前元表没有__tostring方法就直接打印表变量
        print(parameter1)
        print(parameter2)
        print("韬老狮好爱你")
    end
}
myTable3 = {
    name = "韬老狮3"
}
setmetatable(myTable3, myMetaTable3)
myTable3("一刀999", "两刀666")
-- table: 00BA2830
-- 一刀999
-- 两刀666
-- 韬老狮好爱你


print("**********知识点五 元表的运算重载方法************")
--当子表和别的表做运算符操作时时 会默认调用子表的元表中的一些运算符方法 相当于运算符重载
--运算符方法第一个参数是当前子表 第二个参数是和子表操作的表
--注意:子表和别的表使用条件运算符做操作时 子表和别的表要共享同一个元表 即别的表的元表也要设置成当前子表的元表 否则使用条件运算符报错
myMetaTable4 = {
    --运算符+
    __add = function(table1, table2)
        return table1.age + table2.age
    end,
    --运算符-
    __sub = function(table1, table2)
        return table1.age - table2.age
    end,
    --运算符*
    __mul = function(table1, table2)
        return table1.age * table2.age
    end,
    --运算符/
    __div = function(table1, table2)
        return table1.age / table2.age
    end,
    --运算符%
    __mod = function(table1, table2)
        return table1.age % table2.age
    end,
    --运算符^
    __pow = function(table1, table2)
        return table1.age ^ table2.age
    end,
    --运算符==
    __eq = function(table1, table2)
        return table1.age == table2.age
    end,
    --运算符<
    __lt = function(table1, table2)
        return table1.age < table2.age
    end,
    --运算符<=
    __le = function(table1, table2)
        return table1.age <= table2.age
    end,
    --运算符..
    __concat = function(table1, table2)
        return table1.age .. table2.age
    end

}
myTable41 = { age = 100 }
setmetatable(myTable41, myMetaTable4)
myTable42 = { age = 10 }
print(myTable41 + myTable42)          --110
print(myTable41 - myTable42)          --90
print(myTable41 * myTable42)          --1000
print(myTable41 / myTable42)          --19
print(myTable41 % myTable42)          --0
print(myTable41 ^ myTable42)          --1e+020
setmetatable(myTable42, myMetaTable4) --为了使用条件运算符比较 两个表的元表必须是同一个表 否则报错
print(myTable41 == myTable42)         --false
print(myTable41 > myTable42)          --true
print(myTable41 <= myTable42)         --false
print(myTable41 .. myTable42)         --10010


print("**********知识点六 元表的__index表************")
--当子表中想得某一个属性 但找不到对应属性时 会到子表的元表中找__index表变量中有没有对应属性

myMetaTable5 = {
    __index = { name = "韬老狮5" }
}
myTable5 = {

}
setmetatable(myTable5, myMetaTable5)
print(myTable5.name) --韬老狮5 myTable5中没有name 去myMetaTable5中找到__index指向的表 得到里面的name的值
myMetaTable5.__index = { name = "韬老狮55" } --也可以在外面赋值元表的__index变量
print(myTable5.name) --韬老狮55  myMetaTable5在外面重新设置了__index指向的表

--注意:有个小坑 假如元表的__index表变量指向的是自己 假如直接在元表内部声明会打印nil
--所以假如元表的__index指向的是自己建议在外部赋值
myMetaTable6 = {
    name = "韬老狮6",
    __index = myMetaTable6
}
myTable6 = {

}
setmetatable(myTable6, myMetaTable6)
print(myTable6.name)                --nil
myMetaTable6.__index = myMetaTable6 --元表的__index指向的是自己建议在外部赋值
print(myTable6.name)                --韬老狮6

--元表中的__index表变量假如没有对应属性 那么会嵌套的去元表的元表的的__index表变量中找对应属性 还可以一起继续一层一层往上找 直到找不到
myMetaTable7Father = {
    name = "韬老狮7",
}
myMetaTable7 = {
}

myTable7 = {

}
setmetatable(myTable7, myMetaTable7)
setmetatable(myMetaTable7, myMetaTable7Father)
myMetaTable7.__index = myMetaTable7 --假如myTable7找不到属性去myMetaTable7的__index表变量找 即myMetaTable7自己
myMetaTable7Father.__index =
    myMetaTable7Father              --假如myMetaTable7还找不到属性去myMetaTable7Father的__index表变量找 即myMetaTable7Father自己
print(myTable7.name)                --韬老狮7

--不查询元表中的__index表方法 rawget(子表, "要查询的属性") 该方法只会在子表中查找 子表没有就没有了 不会去元表查找__index表了
print(myTable7.name) --韬老狮7 元表的__index表有name属性
print(rawget(myTable7, "name")) --nil 子表没有name属性
myTable7.name = "韬老狮777" --子表设置name属性
print(rawget(myTable7, "name")) --韬老狮777 找到了子表的name属性
print(myTable7.name) --韬老狮7  找到了子表的name属性就不去找元表了


-- 元表的 __index 可以是一个函数 
-- 如果是函数,它会在子表中找不到属性时被调用,并传入两个参数:子表(触发元方法的表)和访问的键。
myMetaTableIndexFunc = {
    __index = function(table, key)
        if key == "greeting" then
            return "Hello from function!"
        else
            return "Key not found"
        end
    end
}
myTableIndexFunc = {}
setmetatable(myTableIndexFunc, myMetaTableIndexFunc)
print(myTableIndexFunc.greeting) -- Hello from function!
print(myTableIndexFunc.otherKey) -- Key not found




print("**********知识点七 元表的__newIndex表************")
--当子表中想改某一个属性并赋值 但找不到对应属性时 会把这个属性赋值到元表的__newindex所指的表中 不会修改子表

myMetaTable8 = {}
myMetaTable8.__newindex = {}
myTable8 = {}
setmetatable(myTable8, myMetaTable8)
myTable8.name = "韬老狮8" --设置了一个myTable7本来并不存在的属性name
print(myTable8.name) -- nil 因为myTable8的元表myMetaTable8中存在__newindex表变量
print(myMetaTable8.__newindex.name) --韬老狮8 name赋值到元表的__newinde表

--不修改元表中的__newIndex表方法 rawset(子表, "要设置的属性", 属性值) 该方法只会在子表设置属性 子表没有该属性 不会去元表设置到__newIndex表上了
print(myTable8.age)                --nil myTable8现在没有age属性
rawset(myTable8, "age", 22)        --设置age属性到子表
print(myMetaTable8.__newindex.age) --nil 元表的__newindex找不到age属性 没有设置到元表的__newindex表中
print(myTable8.age)                --22 子表有age属性了


-- 元表的 __newindex 也可以是一个函数
-- 如果是函数,它会在子表中赋值属性时被调用,并传入三个参数:子表(触发元方法的表)和访问的键和值。
myMetaTableNewIndexFunc = {
    __newindex = function(table, key, value)
        print("Trying to set " .. key .. " to " .. value)
    end
}
myTableNewIndexFunc = {}
setmetatable(myTableNewIndexFunc, myMetaTableNewIndexFunc)
myTableNewIndexFunc.name = "人间自有韬哥在" -- Trying to set name to 人间自有韬哥在


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏