混淆使用“."Lua 中带有 __index 和命名空间的符号 [英] Confusion of using "." notation with __index and namespace in Lua
问题描述
我对使用."的以下两种语法感到困惑
I am confused of the following two syntaxes using "."
据我所知,当键不存在于表中但存在于其元表中时,会调用
__index
.那么为什么列表表调用__index
,然后将自己赋值给list.__index
?
From what I understand,
__index
is called when a key doesn't exist in a table but exists in its metatable. So why does the list table call__index
and then assign itself tolist.__index
?
list = {}
list.__index = list
setmetatable(list, { __call = function(_, ...)
local t = setmetatable({length = 0}, list)
for _, v in ipairs{...} do t:push(v) end
return t
end })
function list:push(t)
if self.last then
self.last._next = t
t._prev = self.last
self.last = t
else
self.first = t
self.last = t
end
self.length = self.length + 1
end
.
.
.
local l = list({ 2 }, {3}, {4}, { 5 })
Window.mt
是否只是创建一个表格?为什么这里需要 Window = {}
作为命名空间?
Does Window.mt
simply create a table? Why do we need Window = {}
as a namespace here?
Window = {} -- create a namespace
Window.mt = {} -- create a metatable
Window.prototype = {x=0, y=0, width=100, height=100, }
function Window.new (o)
setmetatable(o, Window.mt)
return o
end
Window.mt.__index = function (table, key)
return Window.prototype[key]
end
w = Window.new{x=10, y=20}
print(w.width) --> 100
推荐答案
那么为什么列表表会调用 __index ,然后将自己赋值给 list.__index 呢?
您的代码中没有任何地方使用列表调用 __index
.然而,赋值部分是一个常见的 Lua 习语(又名黑客)以节省一些内存.从概念上讲,涉及 4 种不同类型的表:
So why does the list table call __index and then assign itself to list.__index?
Nowhere in your code does the list table call __index
. The assignment part however is a common Lua idiom (aka. hack) to save some memory. Conceptually there are 4 different kinds of tables involved:
- 列出对象(通过代码中的
{length=0}
创建的表) - 一个元表(包含一个
__index
字段),当您尝试访问对象中不存在的字段时,它会修改列表对象的行为 list
类,它包含列表对象的所有方法(如push
方法),同时也作为列表对象的构造函数list
类的元表(包含一个__call
字段),以便您可以像调用list
表一样这是一个函数
- list objects (the tables created via
{length=0}
in your code) - a metatable (containing an
__index
field) that modifies the behavior of list objects when you try to access non-existing fields in the object - the
list
class, which holds all the methods for list objects (like thepush
method), and also serves as a constructor for list objects a metatable (containing a
__call
field) for thelist
class, so that you can call thelist
table as if it were a function
由于元表字段总是以两个下划线开头(__
),而普通方法通常不会,因此您可以将元表字段和普通方法并排放在一个表中而不会发生冲突.这就是这里发生的事情.list
类表也用作列表对象的元表.因此,使用此技巧,您可以节省通常用于单独元表所需的内存(在 x86-64 Linux 上 Lua 5.2 的大小以字节为单位显示在表格标题栏中的方括号中,顺便说一句.):
As metatable fields always start with two underscores (__
), and normal methods usually don't, you can put metatable fields and normal methods side by side into a single table without conflict. And this is what happened here. The list
class table also serves as metatable for list objects. So using this trick you can save the memory you would normally need for the separate metatable (the size in bytes for Lua 5.2 on an x86-64 Linux is shown in square brackets in the table title bars, btw.):
不,{}
创建一个表.但是,这个新表保存在Window
表中的"mt"
键下,可能是为了给这个Window
类"的用户直接访问到用于窗口对象的元表.仅考虑到您展示的代码,这并不是绝对必要的,您可以改用局部变量.
No, {}
creates a table. However, this new table is saved under key "mt"
in the Window
table, probably to give users of this Window
"class" direct access to the metatable that is used for window objects. Given only the code you showed this is not strictly necessary, and you could have used a local variable instead.
原则上,您可以分别存储 Window.mt
、Window.new
和 Window.prototype
,但是如果你有多个类",比如 Window
.这样您就可以避免名称冲突,并且使用 Window
类"看起来更好.
In principle, you could store Window.mt
, Window.new
, and Window.prototype
separately, but that would get cumbersome if you have multiple "classes" like Window
. This way you can avoid name clashes, and using the Window
"class" looks nicer.
另一个原因可能是 require
只能从一个模块定义中返回一个值,如果你想导出多个值(比如 new
、mt
和 prototype
),你需要一个表来将它们包装在一起(或者使用全局变量,但这被认为是不好的风格).
Another reason might be that require
can only return a single value from a module definition, and if you want to export multiple values (like new
, mt
, and prototype
) from a module, you need a table to wrap them together (or use global variables, but that is considered bad style).
这篇关于混淆使用“."Lua 中带有 __index 和命名空间的符号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!