我是 Lua“类”(元表)的新手,我有疑问。
在我编写的以下构造函数代码中,我将变量
obj
声明为 local
。但在网络上的大多数示例中,该变量只是在没有 local
声明的情况下被分配。所以根据我的理解,它变成了一个全局变量(根据我的理解,效率不高)。但这有理由吗?
A = {}
A.__index = A
function A:new(obj_init)
local obj = obj_init or {val = 0}
setmetatable(obj, A)
return obj
end
我还注意到类的成员可以直接访问,甚至可以从另一个 Lua 模块访问:
x = A:new{val = 2}
print(x.val)
但是有没有办法让
val
成为私人会员呢?也许还可以使用local
?
首先,让我们看看您找到的这些示例可能是什么样子。
function A:new(obj)
obj = obj or {val = 0}
...
end
在此代码片段中,
obj
是一个local变量。这是因为Lua中所有函数参数都是局部变量。我们可以将函数重写如下以突出这一点:
function A:new(...)
local obj = ...
obj = obj or {val = 0}
end
我想这就是你所看到的,例如在 PIL 中。您可能将参数重命名为
obj_init
,丢失了 obj
的隐式本地声明。
如果您碰巧消耗了特别糟糕的资源,您可能会看到以下内容:
function A:new(obj_init)
obj = obj_init or {val = 0}
...
end
在这个片段中,
obj
确实是一个全局变量。 由于多种原因,这非常糟糕:
_G
表中的条目,而局部变量存储在 Lua VM 的快速寄存器中。obj
,并期望它在执行过程中不被修改。这可能会导致该函数覆盖全局变量obj
,更糟糕的是,您现在可能无法从构造函数中的协程中产生收益,因为您依赖于可能无法更改的全局状态。在Lua中实现私有表字段的典型方法是按照约定:您可以在字段名称前添加下划线,以表明这些字段不能从外部修改。当然,程序员可以自由地规避这一点。
否则,“私有”变量的概念与 Lua 脚本语言的本质不太吻合;你可以使用元表将成熟的OOP硬塞到Lua上,但这既不惯用,也不高效。
在 Lua 中实现私有成员的最惯用的方法是让它们成为闭包的上值(“访问器”):
A = {}
A.__index = A
function A:new(obj_init)
local obj = {} -- empty object: only member is private
local val = obj_init.val
-- note: this does not need `self`, thus no `:` is used;
-- for setters you might want to discard `self` for consistency
function obj.getVal()
return val
end
setmetatable(obj, A)
return obj
end
x = A:new{val = 2}
print(x.getVal())
-- val can not be set from outside (excepting the debug library);
-- it is "private" and only accessible through the getter method
缺点是访问私有成员的所有函数都必须在每次创建对象时实例化。
请注意,即使上值也不是完全“私有”的,因为它们可以通过调试库访问。
debug
库解决方法debug
库允许您检查堆栈。这使您可以知道哪个方法触发了您的 __index
元方法。因此,您可以向不同的调用者返回不同的值。这对于展示 Lua 元编程功能的概念验证来说可能很好,但在实践中不应该这样做,因为它非常低效和 hacky。