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. winform 窗体关闭枚举类

    switch (e.CloseReason) { case CloseReason.None: break; case CloseReason.WindowsShutDown: break; case ...

  2. JavaWeb学习 (二十六)————监听器(Listener)学习(二)

    一.监听域对象中属性的变更的监听器 域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信 ...

  3. Python和Java分别实现冒泡排序

    1.基本思想 冒泡排序的基本思想是对比相邻的元素值.相邻元素值比较,如果满足条件两者就交换,把较小的移动到前面,把较大的移动到后面,这样较小的元素就像气泡一样浮上来了.可以看出,冒泡排序的每一次循环都 ...

  4. JavaScript跨域解决方式

    平时工作中经常被JavaScript跨域问题所困扰,其实有很多种解决方式,下面给大家介绍常用的几种: 1.jsonp解决跨域问题 客户端代码: <!DOCTYPE html> <ht ...

  5. Shell 示例:利用 $RANDOM 产生随机整数

    代码如下: #!/bin/bash # $RANDOM 在每次调用的时候,返回一个不同的随机整数 # 指定的范围是: 0 - 32767 MAXCOUNT=10 count=1 echo echo & ...

  6. c# 检测是否有Sql非法字符

    /// <summary> /// 检测是否有Sql危险字符 /// </summary> /// <param name="str">要判断字 ...

  7. iomanip的作用 C++

    c++程序里面经常见到下面的头文件 #include <iomanip> 这里面iomanip的作用比较多: 主要是对cin,cout之类的一些操纵运算子,比如setfill,setw,s ...

  8. Java集合框架——容器的快速报错机制 fail-fast 是什么?

    前言:最近看 java 集合方面的源码,了解到集合使用了 fail-fast 的机制,这里就记录一下这个机制是什么,有什么用,如何实现的. 一.fail-fast 简介 fail-fast 机制,即快 ...

  9. linux下安装apache环境

    Centos6.5  64位下安装apache php mysql(安装包版本一定要正确,网上很多都不准,不要用32位的安装包装再64位上,后期有很多问题,下载安装后一定要检出版本,有的网友操蛋,把3 ...

  10. 自定义基于jquery竖向瀑布流插件

    公司新项目做了一个关于图片的板块,网上找了一些瀑布流插件都不是很适合自己,于是就自己造轮子写一个,并封装成插件github 于是就想分享一下,主要是为了更好的学习与记忆. 如果大家进来了,希望能给我g ...