Python Dictionaries |
|
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
-- 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