装饰器和文档字符串 |
|
有时我们想将一些元数据与对象关联起来,例如对象的文档或类型信息。可以将其存储在对象内部的一个字段中,从而修改对象实现,但这可能是需要避免的,尤其是当对象属于他人时(信息隐藏)。实际上,如果对象是函数或只读表,则可能无法修改对象。
一种解决方案基本上是创建一个全局表,将对象(作为键)映射到它们的注解(作为值)。根据定义,对象具有唯一的标识,因此可以用作表中的唯一键。这样,对象本身就不会被修改。这会稍微干扰垃圾回收,因为全局表会持有对象的引用,但我们可以为此目的在 Lua 中使用“弱表”(请参阅《Lua 编程》一书中的 LuaBooks)。
这是将文档字符串 [1] 应用于 Lua 对象的一种方法。
local docstrings = setmetatable({}, {__mode = "kv"}) function document(str) return function(obj) docstrings[obj] = str; return obj end end function help(obj) print(docstrings[obj]) end document[[Print the documentation for a given object]](help) document[[Add a string as documentation for an object]](document) f = document[[Print a hello message]]( function() print("hello") end ) f() help(f)
注意:如果注解对象本身引用了它所注解的对象,则垃圾回收可能会失败(请参阅 GarbageCollectingWeakTables)。
Perl 的“内部对象”模式应用了某种程度上类似的模式。
此模式可应用于其他情况,您希望将元数据应用于对象而不对这些对象进行任何更改。这些包括 Python 中使用的函数装饰器 [2] [3]。
以下应用于函数装饰器的替代语法可能更受欢迎
random = docstring[[Compute random number.]] .. typecheck("number", '->', "number") .. function(n) return math.random(n) end
函数装饰器基本上可以这样实现
mt = {__concat =
function(a,f)
return function(...)
print("decorator", table.concat(a, ","), ...)
return f(...)
end
end
}
function typecheck(...)
return setmetatable({...}, mt)
end
function docstring(...)
return setmetatable({...}, mt)
end
这里在函数装饰器和函数之间使用运算符(..)避免了函数周围的括号,并使得链接装饰器更方便。我们希望此运算符具有右结合性,因此此运算符的唯一选择是 .. 和 ^。
另请参阅 LuaTypeChecking,了解装饰器在表示函数参数和返回类型方面的具体用途。
--DavidManura, 2007-02