Lua基础之MetaTable(6)
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:
- __add: Addition (+)
- __sub: Subtraction (-)
- __mul: Multiplication (*)
- __div: Division (/)
- __mod: Modulos (%)
- __unm: Unary -, used for negation on numbers
- __concat: Concatenation (..)
- __eq: Equality (==)
- __lt: Less than (<)
- __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)的更多相关文章
- Lua 基础
Lua 5.3 的中文手册, http://cloudwu.github.io/lua53doc 在线浏览 --第一部分 -- 两个横线开始单行的注释 --[[ 加上两个[和]表示 多行的注释. -- ...
- Lua基础 函数(一)
转自: http://blog.csdn.net/wzzfeitian/article/details/8653101 在Lua中,函数是对语句和表达式进行抽象的主要方法.既可以用来处理一些特殊的工作 ...
- Step By Step(Lua基础知识)
Step By Step(Lua基础知识) 一.基础知识: 1. 第一个程序和函数: 在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如: /> l ...
- Lua 安全调用 metatable 的简单应用
事情的经过 我们的项目中存在好几个战斗界面,不过界面中的内容略有不同.跟同事出去吃饭的时候,他问我.我们现在的战斗界面.有很多是重复的,但是也有偶尔几个地方不太一样.我在战斗过程中驱动这些界面的时候. ...
- Lua的元表(metatable)
metatable允许我们改变table的行为 > Lua中的每一个表都可以有metatable(后面我们将看到userdata也有Metatable) > Lua默认创建一个不带meta ...
- Lua基础之table详解
概要:1.table特性:2.table的构造:3.table常用函数:4.table遍历:5.table面向对象 原文地址:http://blog.csdn.net/dingkun520wy/art ...
- Lua 基础之Weak Table(5)
Lua垃圾收集策略 Lua自动进行内存的管理.程序只能创建对象,而没有执行删除对象的函数.通过使用垃圾收集技术,Lua会自动删除那些失效的对象,也就是引用为0 的对象.但是呢?有些对象,引用没有指向它 ...
- Lua中的metatable详解
转自:http://www.jb51.net/article/56690.htm Lua 中 metatable 是一个普通的 table,但其主要有以下几个功能: 1.定义算术操作符和关系操作符的行 ...
- lua基础(一)
参考链接: http://blog.csdn.net/lyh916/article/details/49719697 一.注释 --这是行注释 --[[ 这是块注释 这是块注释 这是块注释 --]] ...
随机推荐
- [疑难杂症]__点击win10屏幕最上方的边界会莫名其妙打开Internet Explorer浏览器,不胜其烦(2次ps:已解决!!!).
关于问题描述: 每次误点到屏幕上边界会莫名打卡Internet Explorer浏览器(一开始开以为是自带的Edge浏览器,后来在查找相关解决方法的时候才发现并不是同一款浏览器) 这个问题存在好久了, ...
- 远程连接服务器或云数据库上的mysql服务 - 赖大大
主要问题有两种: 1.mysql的权限问题. 2.服务器的防火墙和数据库的安全组没设好的问题. 1.权限问题: 首先登录上mysql mysql> use mysql; #使用mysq ...
- Spring Boot + Spring Cloud 构建微服务系统(二):服务消费和负载(Ribbon)
使用RestTemplate调用服务 在上一篇教程中,我们是这样调用服务的,先通过 LoadBalancerClient 选取出对应的服务,然后使用 RestTemplate 进行远程调用. Load ...
- Java并发编程笔记之CyclicBarrier源码分析
JUC 中 回环屏障 CyclicBarrier 的使用与分析,它也可以实现像 CountDownLatch 一样让一组线程全部到达一个状态后再全部同时执行,但是 CyclicBarrier 可以被复 ...
- top命令查看进程下线程信息以及jstack的使用
转自:https://www.cnblogs.com/shengulong/p/8513652.html top -Hp pid可以查看某个进程的线程信息 -H 显示线程信息,-p指定pid jsta ...
- U3D GameObject 解读
GameObject本身没有功能,是Unity场景里所有组件的基类,但很多时候我们需要在脚本中操作GameObject.先讲一下GameObject类包含哪些内容,其中常用的用红色标出了 Variab ...
- scikit-learn入门导航
scikit-learn是一个非常强大的机器学习库, 提供了很多常见机器学习算法的实现. scikit-learn可以通过pip进行安装: pip install -U scikit-learn 不过 ...
- rsync算法原理和工作流程分析
本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释.本文不会介绍如何使用rsync命令(见rsync基本用法),而是详细解释它如何实现高效的增 ...
- asp.net 获取网站根地址
public static string GetSiteRoot() { string port = System.Web.HttpContext.Current.Request.ServerVari ...
- 加快maven中jar包的下载速度,maven镜像收集
maven下载jar包的默认仓库是http://my.repository.com/repo/path速度较慢,通过配置国内镜像提高下载速度 1.打开eclipse--->Window---&g ...