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. Python 虚拟环境 | Mac/Linux下如何避坑安装配置Virtualenv

    1.为什么要使用虚拟环境 在Python中,不同的应用可能需要用到不同版本的第三方包,而这些第三方包被统一存放到目录site-packages中,不同版本的包容易相互覆盖,如安装Django 2.1时 ...

  2. apache伪静态配置(URL重写)

    1.打开apache配置文件 httpd.conf .2.开启rewrite模块,去掉注释# #LoadModule rewrite_module modules/mod_rewrite 3.让apa ...

  3. vuejs深入浅出—基础篇

    一.从HelloWorld说起 任何语言的都是从Hello World开始的,VueJs也不例外,直接上代码: <script src="https://unpkg.com/vue/d ...

  4. 页面滚动显示或隐藏元素Headroom.js插件帮助你实现滚动效果

    Headroom.js 是什么? Headroom.js 是一个轻量级.高性能的JS小工具(不依赖任何工具库!),它能在页面滚动时做出响应.此页面顶部的导航条就是一个鲜活的案例,当页面向下滚动时,导航 ...

  5. Ado.net怎么执行存储过程?

    与ADO.Net执行SQL语句的地方只有两点不同1.使用存储过程名代替sql语句2. 使用查询对象SqlCommand,需配置一个CommandType属性 存储过程的执行语法-> exec 存 ...

  6. MVC 【Razor 视图引擎】基础操作 --页面跳转,传值,表单提交

    ASPX  与  Razor  仅仅是视图不一样. 新建项目----ASP.NET MVC 4 Web 应用程序------选择模板(空).视图引擎(Razor ) 1.视图中 c# 代码  与 HT ...

  7. 《Head First设计模式》批注系列(一)——观察者设计模式

    最近在读<Head First设计模式>一书,此系列会引用源书内容,但文章内容会更加直接,以及加入一些自己的理解. 观察者模式(有时又被称为模型-视图(View)模式.源-收听者(List ...

  8. MEF 插件式开发之 小试牛刀

    MEF 简介 Managed Extensibility Framework 即 MEF 是用于创建轻量.可扩展应用程序的库. 它让应用程序开发人员得以发现和使用扩展且无需配置. 它还让扩展开发人员得 ...

  9. jsp使用servlet实现用户登录 及动态验证码

    在进行表单设计中,验证码的增加恰恰可以实现是否为“人为”操作,增加验证码可以防止网站数据库信息的冗杂等... 现在,我将讲述通过servlet实现验证码: 验证码作为一个图片,在页面中为“画”出来的, ...

  10. FormData对象的使用

    一.概述 FormData类型是XMLHttpRequest 2级定义的,它是为序列化表以及创建与表单格式相同的数据提供便利. 作用:1.利用一些键值对来模拟一系列表单控件:即将form中的所有表单元 ...