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提供了内建函数getmetatable
和setmetatable
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+s1
和s1+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
未完待续…
- Python 和 Lua 学习比较 五
- Python 和 Lua 学习比较 一
- Python 和 Lua 学习比较 二
- Python 和 Lua 学习比较 三(上)
- Python 和 Lua 学习比较 三(下)
- Lua,ruby和Python的比较
- lua学习(五)
- Lua学习笔记五
- Lua学习笔记(五)
- Lua学习(五)----表
- LUA学习(五) 迭代器
- lua语言学习五函数
- Python学习五:dict 和 set
- Python学习笔记五:数字和字符串
- Python学习之比较列表和字符串
- lua语言和c++比较
- 五.检查和比较
- Python 和 Lua
- uva 10152
- VMware 虚拟机用host-only模式ping不通主机
- Android WebView详解
- 红米Note调用系统相机拍照后应用崩溃问题分析解决
- 数据库的存储过程
- Python 和 Lua 学习比较 五
- SDUTOJ 2099 - 小型Basic编译器问题(模拟)
- |洛谷|动态规划|P1855 榨取kkksc03
- PHP 获取指定年月日的开始和结束时间戳
- 单片机==矩阵键盘-逻辑键值(11)
- 基于Opencv2.4.11+OpenGL(Qt5.6.0)实现增强现实(三)
- Activity -- Android学习之路
- Transaction rolled back because it has been marked as rollback-only
- HDU 2852KiKi's K-Number 树状数组