Lua基础之MetaTable(6)

转载地址:http://nova-fusion.com/2011/06/30/lua-metatables-tutorial/

关于MetaTable的补充:http://www.cnblogs.com/JesseFang/archive/2012/12/27/2836160.html

In this tutorial I'll be covering a very important concept in Lua: metatables. Knowledge of how to use metatables will allow you to be much more powerful in your use of Lua. Every table can have a metatable attached to it. A metatable is a table which, with some certain keys set, can change the behaviour of the table it's attached to. Let's see an example.

t = {} -- our normal table
mt = {} -- our metatable, which contains nothing right now
setmetatable(t, mt) -- sets mt to be t's metatable
getmetatable(t) -- this will return mt

As you can see, getmetatable and setmetatable are the main functions here; I think it's pretty obvious what they do. Of course, in this case we could contract the first three lines into this:

t = setmetatable({}, {})

setmetatable returns its first argument, therefore we can use this shorter form.

Now, what do we put in these metatables? Metatables can contain anything, but they respond to certain keys (which are strings of course) which always start with __ (two underscores in a row), such as __index and __newindex. The values corresponding to these keys will usually be functions or other tables. An example:

t = setmetatable({}, {
__index = function(t, key)
if key == "foo" then
return 0
else
return table[key]
end
end
})

So as you can see, we assign a function to the __index key. Now let's have a look at what this key is all about.

__index

The most used metatable key is most likely __index; it can contain either a function or table.

When you look up a table with a key, regardless of what the key is (t[4], t.foo, and t["foo"], for example), and a value hasn't been assigned for that key, Lua will look for an __index key in the table's metatable (if it has a metatable). If __index contains a table, Lua will look up the key originally used in the table belonging to __index. This probably sounds confusing, so here's an example:

other = { foo = 3 }
t = setmetatable({}, { __index = other })
t.foo -- 3
t.bar -- nil

If __index contains a function, then it's called, with the table that is being looked up and the key used as parameters. As we saw in the code example above the last one, this allows us to use conditionals on the key, and basically anything else that Lua code can do. Therefore, in that example, if the key was equal to the string "foo" we would return 0, otherwise we look up the table table with the key that was used; this makes t an alias of table that returns 0 when the key "foo" is used.

You may be wondering why the table is passed as a first parameter to the __index function. This comes in handy if you use the same metatable for multiple tables, supporting code re-use and saving computer resources. We'll see an example of this when we take a look at the Vector class.

__newindex

Next in line is __newindex, which is similar to __index. Like __index, it can contain either a function or table.

When you try to set a value in a table that is not already present, Lua will look for a __newindex key in the metatable. It's the same sort of situation as __index; if __newindex is a table, the key and value will be set in the table specified:

other = {}
t = setmetatable({}, { __newindex = other })
t.foo = 3
other.foo -- 3
t.foo -- nil

As would be expected, if __newindex is a function, it will be called with the table, key, and value passed as parameters:

t = setmetatable({}, {
__newindex = function(t, key, value)
if type(value) == "number" then
rawset(t, key, value * value)
else
rawset(t, key, value)
end
end
}) t.foo = "foo"
t.bar = 4
t.la = 10
t.foo -- "foo"
t.bar -- 16
t.la -- 100

When creating a new key in t, if the value is a number it will be squared, otherwise it will just be set anyway. This introduces us to our friends, rawget and rawset.

rawget and rawset

There are times when you need get and set a table's keys without having Lua do it's thing with metatables. As you might guess, rawget allows you to get the value of a key without Lua using __index, and rawset allows you to set the value of a key without Lua using __newindex (no these don't provide a speed increase to conventional way of doing things). You'll need to use these when you would otherwise get stuck in an infinite loop. For example, in that last code example, the code t[key] = value * value would set off the same __newindex function again, which would get you stuck in an infinite loop. Using rawset(t, key, value * value) avoids this.

As you probably can see, to use these functions, for parameters we must pass in the target table, the key, and if you're using rawset, the value.

Operators

Many of the metatable keys available have to do with operators (as in, +, -, etc.), allowing you to make tables support the use of operators on them. For example, say we wanted a table to support the multiplication operator (*), this is how we would do it:

t = setmetatable({ 1, 2, 3 }, {

__mul = function(t, other)

new = {}

for i = 1, other do
for _, v in ipairs(t) do table.insert(new, v) end
end return new

end

})

t = t * 2 -- { 1, 2, 3, 1, 2, 3 }

This allows us to create a new table with the original replicated a certain amount of times using the multiplication operator. As you can tell, the corresponding key for multiplication is __mul; unlike __index and __newindex the keys for operators can only contain functions. The first parameter these functions always receive is the target table, and then the value on the right hand side (except for the unary - which has the key of __unm). Here's a list of the supported operators:

  1. __add: Addition (+)
  2. __sub: Subtraction (-)
  3. __mul: Multiplication (*)
  4. __div: Division (/)
  5. __mod: Modulos (%)
  6. __unm: Unary -, used for negation on numbers
  7. __concat: Concatenation (..)
  8. __eq: Equality (==)
  9. __lt: Less than (<)
  10. __le: Less than or equal to (<=)

    (There's only ==, <, <= because you can implement full support for the comparison operators with just those; in fact only == and < are needed.)

__call

Next comes the __call key, which allows you to call tables as functions. A code example:

t = setmetatable({}, {
__call = function(t, a, b, c, whatever)
return (a + b + c) * whatever
end[enter link description here](https://github.com/kikito/middleclass/blob/master/middleclass.lua#L64)
}) t(1, 2, 3, 4) -- 24

The function in call is passed the target table as usual, followed by the parameters that we passed in.

__call is very useful for many things. One common thing it's used for is forwarding a call on a table to a function inside that table. An example is found in kikito's tween.lua library, where tween.start can also be called by calling the table itself (tween). Another example is found in MiddleClass, where a classes' new method can be called by just calling the class itself.

__tostring

Last of all is __tostring. If implemented, it's used by tostring to convert a table into a string, most handy when using a function like print. Normally, when you try to convert a table to a string, you something in the format of "table: 0x", but you can change that using __tostring. An example:

t = setmetatable({ 1, 2, 3 }, {
__tostring = function(t)
sum = 0
for _, v in pairs(t) do sum = sum + v end
return "Sum: " .. sum
end
}) print(t) -- prints out "Sum: 6"

Building the Vector Class

To wrap everything up, we'll write a class encapsulating a 2D vector (thanks to hump.vector for much of the code). It's too large to put here, but you can see the full code at gist #1055480. I've positioned all the stuff to do with metatables first in the file, as that's the most important stuff. (Be warned, this may be a bit confusing if you've never encountered Object-Oriented Programming before.)

Vector = {}
Vector.__index = Vector

This code sets up the table for the Vector class, and sets the __index key to point back at itself. Now, what's going on here? You've probably noticed that we've put all the metatable methods inside the Vector class. What you're seeing is the simplest way to achieve OOP (Object-Oriented Programming) in Lua. The Vector table represents the class, which contains all the functions that instances can use. Vector.new (shown below) creates a new instance of this class.

function Vector.new(x, y)
return setmetatable({ x = x or 0, y = y or 0 }, Vector)
end

It creates a new table with x and y properties, and then sets the metatable to the Vector class. As we know, Vector contains all the metamethods and especially the __index key. This means that we can use all the functions we define in Vector through this new table. We'll come back to this in a moment.

Another important thing is the last line:

setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })

This means that we can create a new Vector instance by either calling Vector.new or just Vector.

The last important thing that you may not be aware of is the colon syntax. When we define a function with a colon, like this:

function t:method(a, b, c)
-- ...
end

What we are really doing is defining this function:

function t.method(self, a, b, c)
-- ...
end
```
This is syntactic sugar to help with OOP. We can then use the colon syntax when calling functions:
```
-- these are the same
t:method(1, 2, 3)
t.method(t, 1, 2, 3)
Now, how do we use this Vector class? Here's an example: a = Vector.new(10, 10)
b = Vector(20, 11)
c = a + b
print(a:len()) -- 14.142135623731
print(a) -- (10, 10)
print(c) -- (30, 21)
print(a < c) -- true
print(a == b) -- false
``` Because of the __index in Vector, we can use all the methods defined in the class through the instances. #### Conclusion
Thanks for reading, I hope you've learned something. If you have any suggestions or comments, please leave them in the comments section below; I'd love to hear from you! #####补充:
Lua5.1支持userdata中使用__gc元方法,Lua5.2之后对普通table也支持,在设置Api交互的时候比较便利。

Lua基础之MetaTable(6)的更多相关文章

  1. Lua 基础

    Lua 5.3 的中文手册, http://cloudwu.github.io/lua53doc 在线浏览 --第一部分 -- 两个横线开始单行的注释 --[[ 加上两个[和]表示 多行的注释. -- ...

  2. Lua基础 函数(一)

    转自: http://blog.csdn.net/wzzfeitian/article/details/8653101 在Lua中,函数是对语句和表达式进行抽象的主要方法.既可以用来处理一些特殊的工作 ...

  3. Step By Step(Lua基础知识)

    Step By Step(Lua基础知识) 一.基础知识:    1. 第一个程序和函数:    在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如:    /> l ...

  4. Lua 安全调用 metatable 的简单应用

    事情的经过 我们的项目中存在好几个战斗界面,不过界面中的内容略有不同.跟同事出去吃饭的时候,他问我.我们现在的战斗界面.有很多是重复的,但是也有偶尔几个地方不太一样.我在战斗过程中驱动这些界面的时候. ...

  5. Lua的元表(metatable)

    metatable允许我们改变table的行为 > Lua中的每一个表都可以有metatable(后面我们将看到userdata也有Metatable) > Lua默认创建一个不带meta ...

  6. Lua基础之table详解

    概要:1.table特性:2.table的构造:3.table常用函数:4.table遍历:5.table面向对象 原文地址:http://blog.csdn.net/dingkun520wy/art ...

  7. Lua 基础之Weak Table(5)

    Lua垃圾收集策略 Lua自动进行内存的管理.程序只能创建对象,而没有执行删除对象的函数.通过使用垃圾收集技术,Lua会自动删除那些失效的对象,也就是引用为0 的对象.但是呢?有些对象,引用没有指向它 ...

  8. Lua中的metatable详解

    转自:http://www.jb51.net/article/56690.htm Lua 中 metatable 是一个普通的 table,但其主要有以下几个功能: 1.定义算术操作符和关系操作符的行 ...

  9. lua基础(一)

    参考链接: http://blog.csdn.net/lyh916/article/details/49719697 一.注释 --这是行注释 --[[ 这是块注释 这是块注释 这是块注释 --]] ...

随机推荐

  1. Hashtable,HashMap,TreeMap有什么区别?Vector,ArrayList,LinkedList有什么区别?int和Integer有什么区别?

    接着上篇继续更新. /*请尊重作者劳动成果,转载请标明原文链接:*/ /*https://www.cnblogs.com/jpcflyer/p/10759447.html* / 题目一:Hashtab ...

  2. Spring Boot初识(4)- Spring Boot整合JWT

    一.本文介绍 上篇文章讲到Spring Boot整合Swagger的时候其实我就在思考关于接口安全的问题了,在这篇文章了我整合了JWT用来保证接口的安全性.我会先简单介绍一下JWT然后在上篇文章的基础 ...

  3. Vue.js的复用组件开发流程

    本文由蔡述雄发表 接下来我们会详细分析下如何完成由多个组件组成一个复用组件的开发流程. 下面先看看我们的需求 列表组件quiList.vue 本节我们主要要完成这样一个列表功能,每一行的列表是一个组件 ...

  4. Nginx/Apache之伪静态设置 - 运维小结

    一.什么是伪静态伪静态即是网站本身是动态网页如.php..asp..aspx等格式动态网页有时这类动态网页还跟"?"加参数来读取数据库内不同资料,伪静态就是做url重写操作(即re ...

  5. DNS服务器能遭受到的DDNS攻击类型

    每个网站都有可能会遭受到攻击,现在的互联网服务器遭受的最多的是DDOS攻击,本文总结了一些DNS服务器会遇到的DDOS攻击类型,以及相应的预防措施. [1]的作者将DDOS的攻击分为三种类型: 大数据 ...

  6. Hyperledger Fabric密码模块系列之BCCSP(一)

    Fabric作为IBM主导的区块链平台,可谓是联盟链中的一枝独秀,现如今已经有100多个大型国际银行.金融以及科技公司的加盟.与其说Fabric是区块链的一种平台,倒不如说是一个区块链框架更加精确,因 ...

  7. easyui datagrid 禁止选中行

    没有找到可以直接禁止的属性,但是找到两个间接禁止的方式. 方式一: //onClickRow: function (rowIndex, rowData) {     // $(this).datagr ...

  8. php 中的sprintf 坑

    先说下为什么要写这个函数的前言,这个是我在看工作中发现一处四舍五入的bug后,当时非常不理解, echo sprintf('%.2f',123.455); //123.45 echo sprintf( ...

  9. 【Java】XML文件的解析

    import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  10. JQuery实现省市区的三级联动

    JQuery实现省市区的三级联动 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "h ...