Python Dictionaries |
|
settagmethod
) are no longer present in Lua 5 but have been replaced with metamethods.
Lua's table type is a multipurpose container which is somewhere in between Pythons list and dictionary types. The listing below emulates Pythons dictionaries. See the documentation on Pythons mapping types to what is implemented here [1]. See PythonLists and ClassesAndMethods for more information on how it is implemented. The script was written for Lua version 4.0.
This code was an exercise in trying to emulate the functionality of Python using Lua. It threw up some problems with Lua which seem to have been resolved in Lua 4.1. Comments on this implementation :-
foo = Dict:new { len="vcbvcb" } print(foo:len())The colon operator accesses foo, not Dict. You redirect missing elements (methods) to Dict but if the name already exist in foo that one will be taken. Read [2] for a possible (already implemented and working) solution. -- ET
settagmethod(tag(Dict), "gettable", function(t,k) if rawget(Dict,k) then return rawget(Dict,k) else return rawget(t,k) end end )
foo:len()
will give 1, but foo.len
will give a function, not the stored value "vcbvcb". Btw, PythonLists only works because there are no collisions between user indexes (always numbers) and methods (always strings). -- ET
Code:
-- Emulation of Python dictionaries -- Nick Trout -- thanks to lhf & ET -- See http://www.python.org/doc/current/lib/typesmapping.html -- $Header: /Tools/build/pydict.lua 3 11/09/01 14:20 Nick $ Dict = settag({},newtag()) -- handle access to our dictionary table type function Dict._gettable(t,k) -- print("gt",t,k) -- See if the key we are looking for is a method in Dict. -- Note the user may have used a key which has the same name as -- one of our functions, but methods take precidence . eg. -- foo = Dict:new { len="vcbvcb" } print(foo:len()) local v = rawget(Dict,k) if v then return v else -- In Python, if we dont find a key in a dictionary we raise a Key error. v = rawget(t,k) assert(v,"Key error") return v end end settagmethod(tag(Dict), "gettable", Dict._gettable) -- Create a new dictionary. -- eg. dict = Dict:new() or dict = Dict:new{ a=1,b=2 } function Dict:new(t) if not t then t={} end settag(t,tag(Dict)) return t end -- len(a) the number of items in a function Dict:len() -- Note: Lua returns the number of indexed objects, not mapped objects with getn local cnt=0 for k,v in self do cnt=cnt+1 end return cnt end -- Python: a[k] the item of a with key k (1) -- Lua: dict[k] -- Python: a[k] = v set a[k] to v -- Lua: dict[k] = v -- Python: del a[k] remove a[k] from a (1) -- Lua: dict:del(k) function Dict:del(k) self[k] = nil end -- Python: a.clear() remove all items from a -- Lua: dict:clear() function Dict:clear() -- cannot do self = {} as self passed by value -- we cannot change a table inside a for loop -- eg. for k,v in self do self[k]=nil end -- Must collect keys and delete them thus: local t={} for k,v in self do t[k]=1 end for k,v in t do self[k]=nil end end -- Python: a.copy() a (shallow) copy of a -- Lua: dictcopy = dict:copy() function Dict:copy() local d = Dict:new() for k,v in self do d[k] = v end return d end -- Python: k in a 1 if a has a key k, else 0 -- k not in a 0 if a has a key k, else 1 -- a.has_key(k) Equivalent to k in a function Dict:has_key(k) return self[k] -- return value for true, or nil for false end -- Python: a.items() a copy of a's list of (key, value) pairs function Dict:items() local items={} for k,v in self do tinsert(items,{k,v}) end return items end -- Python: a.keys() a copy of a's list of keys function Dict:keys() local keys={} for k,v in self do tinsert(keys,k) end return keys end -- Python: a.update(b) for k in b.keys(): a[k] = b[k] -- Add b to a function Dict:update(b) assert(type(b)=="table") for k,v in b do self[k] = v end end -- Python: a.values() a copy of a's list of values function Dict:values() local vals={} for k,v in self do tinsert(vals,v) end return vals end -- Python: a.get(k[, x]) a[k] if k in a, else x -- Return the value associated with key k or x is key not found function Dict:get(k,x) -- use rawget to avoid invoking "index" tag method if k not found return rawget(self,k) or x end -- Python: a.setdefault(k[, x]) a[k] if k in a, else x (also setting it) -- Set value for k to x if k not found, also return value function Dict:setdefault(k,x) self[k] = rawget(self,k) or x return self[k] end -- Python: a.popitem() remove and return an arbitrary (key, value) pair function Dict:popitem() local k,v = next(self) self[k] = nil return k,v end -- Python len(list) is not the same as getn, must count key-value pairs len = Dict.len -- test using: lua -f pydict.lua -test if arg and arg[1]=="-test" then local prl = function(l) for i=1,getn(l) do write(l[i]) end print() end local prd = function(l) for k,v in l do write(k.."="..v..",") end print() end local dict = Dict:new{a=1,b=2,c=3} prd(dict) dict["d"]=4 ; write("d=4: ") ; prd(dict) dict.e=5 ; write("e=5: ") ; prd(dict) print("dict length: "..dict:len()) dict:del(3) ; write("del[3]: ") ; prd(dict) local d2 = dict:copy() ; write("copy: ") ; prd(d2) d2:clear() ; write("clear: ") ; prd(d2) print("length: "..d2:len()) assert( d2:len()==0 ) assert( dict:has_key("a") ) print('dict:has_key("a") : '..dict:has_key("a")) write("items: ") ; print( getn(dict:items()) ) write("keys: ") ; prl( dict:keys() ) dict:update{ f=6,g=7 } ; write("dict:update{ f=6,g=7 } : ") ; prd(dict) write("values: ") ; prl( dict:values() ) write('dict:get("z",26) : ') print(dict:get("z",26)) write('dict:setdefault("y",25) : ') print(dict:setdefault("y",25)) write('dict:popitem() : '..dict:popitem()..", ") ; prd(dict) local foo = Dict:new { len="vcbvcb" } print(foo:len()) -- same name test -- print(foo["wont find this"]) -- test "key error" end