Python 和 Lua 学习比较 五

来源:互联网 发布:4g网络制式是什么意思 编辑:程序博客网 时间:2024/04/28 03:07

模块

前言

不管是python或者lua,我们打开命令窗口,然后赋值一些变量比如a=1,之后关闭窗口然后再打开,打印a的时候我们得到的是未赋值。
如果我们想要长久的程序,我们就需要写脚本。为了便于维护,我们可能需要写不同的脚本文件。
这时候我们写的脚本文件也可以称之为模块了。文件中的函数我们也可以引用进来并使用。
python文件使用.py做后缀。在模块中,__name__指代我们的模块名。
lua文件使用.lua做后缀。在lua脚本中,...代表一些我们脚本参数

python模块申明和使用

我的平台是windows,所以我先打开命令窗口,先进入我的测试目录cd E:\script\python,E:。然后编写我的脚本文件fibo.py
这里写图片描述

fibo.py内容:

#注释#Fibonacci numberdef fib(n):    print(__name__)    a,b = 0,1    while b<n:        print(b,end=' ')        a,b = b,a+b    print()def fib2(n):    result = []    a,b = 0,1    while b<n:        result.append(b)        a,b = b,a+b    return result

现在我们引入这个模块:

>>> import fibo #注意这里不要加后缀。>>> fibo.fib(3)fibo1 1 2>>> fibo.__name__'fibo'>>> fib2 = fibo.fib2 #赋值给本地变量,方便访问>>> fib2(80)[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

python模块可以包含一个可执行语句,在引入的时候初始化,并且只执行一次。
每个模块都有自己的标识表,我们访问模块的函数或者变量时:modname.itemname

模块还能引入别的模块,但是不一定要使用import 语句引入模块。
还可以这样: from...import...引入指定名字

>>> from fibo import fib, fib2>>> fib(400)1 1 2 3 5 8 13 21 34 55 89 144 233 

上面这个语法将不会引入模块,而是直接引入模块函数,所以上面 fibo是非法的。

还可以:

>>> from fibo import *>>> fib(400)1 1 2 3 5 8 13 21 34 55 89 144 233

上面这个方法引入模块所有的名字,除了下划线开头的名字;当然fibo模块名也是非法的。在通常情况下,我们不会用这个方法引入模块,因为这将导致我们引入一些我们不需要的名字,或者将我们已经定义的名字覆盖。

出于性能考虑,对每个模块,python解释器只引入一次;如果你在引用后做了修改,需要重启python解释器或者使用imp.reload():import imp; imp.reload(modulename).
重新加载只能是模块名(必须已经成功加载的),不能重载函数。

值得一提的是,我们import模块的时候,python解释器其实是把我们的脚本编译了一遍,并放在当前目录的__pycache__
这里写图片描述

我们也这样可以执行脚本:python fibo.py <arguments>,arguments代表参数的意思。
现在我们的fibo.py是这样:

#注释#Fibonacci numberdef fib(n):    print(__name__,1,2)    a,b = 0,1    while b<n:        print(b,end=' ')        a,b = b,a+b    print()def fib2(n):    result = []    a,b = 0,1    while b<n:        result.append(b)        a,b = b,a+b    return resultdef _add(a,b): # 这个函数 在 from fibo import * 的时候将不会被引入,类似私有的性质。    return a+bprint(1,__name__)if __name__ == "__main__": # 这里执行脚本的时候会执行    print(2,__name__)    import sys    fib(int(sys.argv[1]))

现在执行我们的脚本:

E:\script\python>python fibo.py 51 __main__2 __main____main__ 1 21 1 2 3

如果我们只是引入模块:

>>> import fibo1 fibo

上面判断__name__的值是否等于"__main__"的机理就是我们测试我们模块的功能单元。

lua模块申明和使用

lua没有提供类似python的模块机制,或者像其他语言,java和perl的packages,或者c++里面的namespaces等等。但是lua也很容易做到上面类似的事情,就是把table当package用。lua的基础库就是这么干的~。
一个明显的好处就是我们可以使用lua本身table的各项功能,我们可以像操作table一样操作我们的”package”,而且还能把这个”package”赋值给其他变量,或者作为参数传递给函数。听着是不是很棒!(在很多语言中,packages不是作为类的存在)

下面我们写一个操作复数的lua脚本:

--定义包的私有函数--检查复数local function checkComplex(c)    if not ((type(c) == "table") and tonumber(c.r)        and tonumber(c,i)) then        -- 报错        error("bad complex number",3)    endend--构造一个复数local function new(r,i) return {r=r,i=i} end--申明一个标准i--P.i = new(0,1)--复数加法local function add(c1,c2)    checkComplex(c1)    checkComplex(c2)    return new(c1.r+c2.r,c1.i+c2.i)end--复数减法local function sub(c1,c2)    return new(c1.r-c2.r,c1.i-c2.i)end--复数乘法local function mul(c1, c2)  return new(c1.r*c2.r - c1.i*c2.i,                     c1.r*c2.i + c1.i*c2.r)end-- 自定义需要导出的函数,或者变量-- lua不能像其他语言那样,把申明放在开始的位置,它需要先申明函数。-- 这样做的目的是为了包内函数之间相互调用不用加前缀“complex”complex = {    new = new,    add = add,    sub = sub,    mul = mul,    i = new(0,1),}-- 这个返回语句不是必须的。因为这个包已经赋-- 值给全局变量complex中,但是建议你这样做return complex 

脚本保存在 E:\script\lua中,打开控制台命令,定位到该目录,调起lua:

> require "complex" -- 引入我们的复数包,require对同一个包只引入一次table: 00000000004c9d40> complextable: 00000000004c9d40> complex.itable: 00000000004c9d80> complex.i.r0> complex.i.i1> complex.newfunction: 00000000004caff0> complex.addfunction: 00000000004c9d00> complex.subfunction: 00000000004cb080> complex.mulfunction: 00000000004cb0b0> complex.add(1,2)--因为不是复数,所以调用报错。bad complex numberstack traceback:        [C]: in function 'error'        .\complex.lua:8: in upvalue 'checkComplex'        .\complex.lua:20: in function 'complex.add'        (...tail calls...)        [C]: in ?

最好的习惯就是保持我们的lua文件名跟我们的lua包名一致。

require语句的搜索路径是根据package.path去搜索的,我们可以看下package.path:

> package.pathD:\sdk\lua\lua\?.lua;D:\sdk\lua\lua\?\init.lua;D:\sdk\lua\?.lua;D:\sdk\lua\?\init.lua;D:\sdk\lua\..\share\lua\5.3\?.lua;D:\sdk\lua\..\share\lua\5.3\?\init.lua;.\?.lua;.\?\init.lua

里面的问号就是require后面跟的内容。

python模块更多信息

python模块查找原理:当一个模块 A 被引入的时候,python解释器首先先查找内建模块,然后在sys.path给的路径列表搜索A.py文件。
sys.path目录构成有:

  • 脚本引入目录(当前目录)
  • PYTHONPATH指定目录
  • installation-dependent 默认目录

编译过的python文件
为了提高模块的加载速度,python缓存了版本编译过的模块,放在__pycache__目录中,命名:module.version.pyc,这个文件是跨平台的。(文章上面已经展示图片)
python编译也是增量编译的,会检测源文件距离上次编译是否修改。
python在两种情况下不会检测缓存,第一种是没有缓存的文件夹;第二是都使用了已经编译的模块。

如果我们编译python文件的时候,可以加上-O或者-OO参数来优化,生成了module.version.pyo。-O优化掉了assert语句,-OO优化掉了assert语句还有__doc__字符串.他们之间的运行速度并没有变化,只是加载的速度不同,当然优化后编译的文件会缩小。
编译命令:

python -m compileall fibo.py #编译pyc文件,二进制文件,保护代码python -OO -m compileall fibo.py #编译pyo文件,二进制文件,保护代码

python 提供了很多内建模块方便我们使用。如sys,它有很多函数,常人是很难都记住的,如果不查阅文档,怎么能快速的浏览呢?
python为我们提供了dir函数:

>>> dir(sys)['__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe','_home', '_mercurial', '_xoptions', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats','copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions', 'winver']

如果dir不传参数,那么默认输出当前定义的变量,模块,函数:

>>> dir()['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'sys']

但是上面没有包含当前内建的函数和变量。如果我们想要看一下:

>>> dir(builtins)['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError','KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__','__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

python包
包可以结构化python的模块,通过.访问,类似Java,A.B 意味着有一个B模块在A包里。
包的一般结构是这样的:

sound/                          Top-level package      __init__.py               Initialize the sound package      formats/                  Subpackage for file format conversions              __init__.py              wavread.py              wavwrite.py              aiffread.py              aiffwrite.py              auread.py              auwrite.py              ...      effects/                  Subpackage for sound effects              __init__.py              echo.py              surround.py              reverse.py              ...      filters/                  Subpackage for filters              __init__.py              equalizer.py              vocoder.py              karaoke.py              ...

目录中的__init__.py是必须的文件,内容可以为空,也可以是包的一些初始化代码,或者__all__变量,后面将介绍。

包的使用跟模块差不多,类似:

import package1.package2.xxxfrom package1.package2 import xxx

使用from package import item格式的时候,item可以是子模块(子包),也可以是模块函数,类或者变量,import语句先检查item是否是包的定义,如果没找到,那么检查是否是模块的定义,并尝试加载它,如果没找到,那么报ImportError.
但是如果用import item.subitem.subsubitem的时候,除了最后一个item,其他item都必须是包(package);最后一个item可以是模块或者是包,但不能是之前定义的类、函数、变量。

from package.subpackage import *,这个语句跟模块不同,它不能把包下面的子包都引入,需要我们在__init__.py文件中定义 __all__这个加载列表,比如我们的文件sound/effects/__init__.py 可以这样定义我们需要加载的模块:

#指定我们需要加载的包__all__ = ["echo", "surround", "reverse"]

包内的相互引用:比如我们的 sound.filters.vocoder 需要 sound.effects 包里的 echo 模块,我们可以这样申明引用:from sound.effects import echo;也可以用相对路径: . 代表当前路径,.. 代表上一层路径;

#我们可以这样使用from . import echofrom .. import formatsfrom ..filters import equalizer

最后一个包的功能是 __path__,它可以指定一个list,它可以为我们的包提供更多的搜索路径,但是这个不常用,一般都是我们的包当前路径。

lua table 更多内容

既然我们lua都已经讲到模块了,那么我们有必要补充一下metatable (元表)的知识了。
lua中的table拥有一些列可预见的操作。
比如申明一个普通的table:a = {x=1,y=2},如果我们访问a.x,那么打印1,如果我们访问a.y,那么打印2,如果我们访问a.z,那么打印nil。但是如果我们想要a.z有值呢?当然不是简单的a.z = xx赋值这么简单的方法。这时候就需要我们的元表了。
lua提供了内建函数getmetatablesetmetatable
lua的元表可以是自身,也可以是别的table:

> a = {x=1,y=2}> setmetatable(a,a) -- 设置a的元表为自己table: 003ea930 -- 返回table a> getmetatable(a) -- 返回a的元表table: 003ea930> b = {z=3}> setmetatable(a,b) -- 设置a的元表为btable: 003ea930> getmetatable(a)table: 003eb328> a.z -- 猜猜看我们能打印出3吗?

好,既然上面不能打印3那就疑问了,我们设置这个干啥呢?这个需要配合元方法使用!上面这个例子后面还会说,我们先来看最简单的元方法-算术元方法。
举个例子,通常我们想要两个table实现+运算是不行的:

> a = {1}> b = {2}> a + bstdin:1: attempt to perform arithmetic on a table value (global 'a')stack traceback:        stdin:1: in main chunk        [C]: in ?

好,下面我们来定义一个集合,保存为set.lua文件:

Set = {}Set.mt = {}function Set.new(t)    local set = {}    setmetatable(set,Set.mt)    for _,v in pairs(t) do         table.insert(set,v)     end    return setendfunction Set.add(a,b)    local ret = Set.new{} -- 等价于 Set.new({}),以前提过    for _,v in pairs(a) do         table.insert(ret,v)      end    for _,v in pairs(b) do         table.insert(ret,v)      end    return retendSet.mt.__add = Set.add -- 注意这里的 __add

我们引入这个模块,然后执行我们的加法:

> require "set"true> s1 = Set.new{1,2}> s2 = Set.new{4,5}> s3 = s1 + s2> for k,v in pairs(s3) do print(k,v) end1       12       24       45       5

到这里,我们发现我们可以加法了呢。。。为啥呢???
仔细看我们上面的元表Set.mt,我们对它增加了一个方法Set.mt.__add,如果我们尝试把它赋值为nil:

> Set.mt.__add = nil> s1 + s2 -- 报错了。stdin:1: attempt to perform arithmetic on a table value (global 's1')stack traceback:        stdin:1: in main chunk        [C]: in ?> Set.mt.__add = Set.add -- 这里再改回来!

所以呢,我们两个Set能相加,多亏了我们的__add方法。我们称之为元方法,类似的方法还有很多:

__add    重载 +  -- 类似C++中的重载操作符。__sub    重载 -__mul    重载 *__div    重载 /__unm    重载 not__pow    重载 ^__concat 重载 ..

另外,lua对于自定义类型(table)的操作运算遵循:
1. 如果第一个值有元表,并且有对应的操作符重载,lua选择它作为这次运算的方法。不依赖第二个值
2. 否则,如果第二个值有元表,并且有对应的操作符重载,lua选择它作为这次运算的方法。
3. 否则,lua raises an error(报错)

按照上面的规则,1+s1s1+1是等价的,想一想为什么?

如果我们直接去运算 s1+1会报错,我们需要加条件判断:

function Set.add(a,b)    -- 增加对a和b的判断    if getmetatable(a) ~= Set.mt or getmetatable(b) ~= Set.mt then        error("attempt to `add' a set with a non-set value")    end    local ret = Set.new{} -- 等价于 Set.new({})    for _,v in pairs(a) do         table.insert(ret,v)      end    for _,v in pairs(b) do         table.insert(ret,v)      end    return retend

再试一下:

> s1 + 1 -- 现在就是报我们自己的error了.\set.lua:17: attempt to `add' a set with a non-set valuestack traceback:        [C]: in function 'error'        .\set.lua:17: in metamethod '__add'        stdin:1: in main chunk        [C]: in ?

还有关系元方法:

__eq 重载 ==  -- 没有~=,可以用 not ==__lt 重载 <   -- 没有>,可以换个方向 __le 重载 <=  -- 没有>=,可以换个方向 

改造一下我们的脚本:

function Set.check(a,b)    -- 增加对a和b的判断    if getmetatable(a) ~= Set.mt or getmetatable(b) ~= Set.mt then        error("attempt to operate a set with a non-set value")    endendfunction Set.le(a,b)    Set.check(a,b)    return #a <= #bendfunction Set.lt(a,b)    return a <= b and not (b <= a)endfunction Set.eq(a,b)    return a <= b and b <= aendSet.mt.__le = Set.leSet.mt.__lt = Set.ltSet.mt.__eq = Set.eq

退出重新引入一下:

> Set.new{1,2,3} == Set.new{4,5,6} -- 长度一致true> {1,2,3} == {4,5,6} -- 没有重载就会去判断对象是否同一个false> {1,2,3} == {1,2,3} -- 这里跟python不同false

关系元方法跟算术元方法不一样,它不支持混合类型。如果你比较两个不同类型的数据(string和number)的大小,或者拥有不同关系元方法的对象的大小,lua将报错,但是==是另外。==的两边如果是不同类型的数据,那么直接返回false,或者都是table,但是他们的关系元方法不一样,那么也是返回false,仅当都是对象(table),且关系元方法一致,那么lua将会调用这个元方法。

还有一些其他的元方法:

__tostring  重载内建函数 tostring()__metatable 重载内建函数 getmetatable()

看例子:
对我们的脚本增加下面代码:

function Set.checkset(a)    if getmetatable(a) ~= Set.mt then        error("attempt to operate with a non-set value")    endendfunction Set.tostring(a)    Set.checkset(a)    local ret = "{"    local sep = ""    for k,v in pairs(a) do         ret=ret..sep..v        sep = ","    end    return ret.."}"end

重新引入:

> s1 = Set.new{1,2,3}> s1{1,2,3} -- 这就是我们想要的打印.-- 如果不想别人修改你的metatable,那么我们可以定义一个__metatable> Set.mt.__metatable = "not your business"> getmetatable(s1)not your business> setmetatable(s1,s1) stdin:1: cannot change a protected metatablestack traceback:        [C]: in function 'setmetatable'        stdin:1: in main chunk        [C]: in ?-- 无法修改保护的metatable,OK目的达成

以上基础元方法已经介绍完毕,最后看两个特殊的元方法__index__newindex,它们就可以完成我们一开始的a.z任务^_^
所有的table,它们在访问成员的时候其实通过__index
方法去找的,如果找到返回这个值,如果没找到返回nil。
先来看看我们的最新脚本文件set.lua:

Set = {}Set.mt = {}Set.mt.__metatable = "sorry abourt this"function Set.new(t)    local set = {}    setmetatable(set,Set.mt)    for _,v in pairs(t) do         table.insert(set,v)     end    return setendfunction Set.checkset(a)    if getmetatable(a) ~= Set.mt.__metatable then        error("attempt to operate with a non-set value")    endendfunction Set.check(a,b)    -- 增加对a和b的判断    if getmetatable(a) ~= Set.mt.__metatable         or getmetatable(b) ~= Set.mt.__metatable then        error("attempt to operate a set with a non-set value")    endendfunction Set.add(a,b)    Set.check(a,b)    local ret = Set.new{} -- 等价于 Set.new({})    for _,v in pairs(a) do         table.insert(ret,v)      end    for _,v in pairs(b) do         table.insert(ret,v)      end    return retendfunction Set.le(a,b)    Set.check(a,b)    return #a <= #bendfunction Set.lt(a,b)    return a <= b and not (b <= a)endfunction Set.eq(a,b)    return a <= b and b <= aendfunction Set.tostring(a)    Set.checkset(a)    local ret = "{"    local sep = ""    for k,v in pairs(a) do         ret=ret..sep..v        sep = ","    end    return ret.."}"endSet.mt.__add = Set.addSet.mt.__le = Set.leSet.mt.__lt = Set.ltSet.mt.__eq = Set.eqSet.mt.__tostring = Set.tostring

重新引入文件,然后看这个例子:

> s1 = Set.new{1,2,3} -- 申明一个新的Set> s1 -- 看打印{1,2,3}-- 我们申明__index函数,打印参数table和key,并返回nil> Set.mt.__index = function(tab,key) print(tab,key) return nil end> s1.a -- 跟原来一样a> s1.b -- 区别来了。。。{1,2,3,a}       bnil

通过上面的例子我们看到,当调用s1里面含有的key的时候跟原来一样,但是s1.b的时候,由于我们的s1里面并不含有,所以就调用了我们给__index赋值的函数,打印输出,当然结果还是nil.
回到最开始的例子(a.z):

> a = {x=1,y=2}> b = {z=3}> setmetatable(a,b)table: 003eee58> b.__index = function(tab,key) return b[key] end> a.z3 -- OK 我们想要的结果来了~

其实我们的__index可以是function也可以是table,如果是function,那么会把调用的table和key作为参数传过去;如果是table,那么lua只是重新查询,如果查到返回,没有则继续调用这个table的__index元方法。
利用这一点我们可以完成类的继承这样的功能。具体后面再细说。
如果我们只是想访问这个table的元素,避开它的__index调用,我们可以调用内建函数rawget(table,key):

> rawget(a,"x") -- 避开__index1> rawget(a,"z") -- 避开__indexnil

rawget函数并不能给我们的程序提速,但是我们有时候需要用它。

__newindex元方法是更新table,当我们给table赋值一个本不存在的key的时候,lua解释器先去找这个table的__newindex,如果不为nil,则调用它;如果为nil,就对table做赋值操作:

> a = {}> setmetatable(a,a)table: 0033aa88> a.__newindex = function(tab,k,v) return print(tab,k,v) end> a.x = 1table: 0033aa88 x       1>a.xnil

__newindex的值可以是table,这样赋值操作将会在那个table上发生,原table不会做任何事。

> a = {}> b = {}> setmetatable(a,b)table: 00c6f0a0> b.__newindex = b -- 想想这里能不能赋值 a ?> a.x = 1> a.xnil> b.x1> rawset(a,'y',2) -- 这个跟rawget有点类似,避开__newindextable: 0068a9c0> a.y2> b.ynil> b.__index = b> a.x1

table的默认值是nil,我们可以用__index修改这个默认值,想想可以怎么做?

利用__index__newindex我们可以监视某个空table的操作:

-- create private indexlocal index = {}-- create metatablelocal mt = {  __index = function (t,k)    print("*access to element " .. tostring(k))    return t[index][k]   -- access the original table  end,  __newindex = function (t,k,v)    print("*update of element " .. tostring(k) ..                         " to " .. tostring(v))    t[index][k] = v   -- update original table  end}function track (t)--监视函数,要监视某个table,只需t=track(t)  local proxy = {}  proxy[index] = t  setmetatable(proxy, mt)  return proxyend

把table变为只读:

function readOnly (t)  local proxy = {}  local mt = {       -- create metatable    __index = t,    __newindex = function (t,k,v)      error("attempt to update a read-only table", 2)    end  }  setmetatable(proxy, mt)  return proxyend

使用方法:

days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}print(days[1])     --> Sundaydays[2] = "Noday"stdin:1: attempt to update a read-only table

未完待续…

0 0