Python Dictionaries

lua-users home
wiki

[!] VersionNotice: The below code pertains to an older Lua version, Lua 4. Certain features used like tag methods (settagmethod) are no longer present in Lua 5 but have been replaced with metamethods.

Introduction

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 :-

Sorry, this will not work. Try:

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

So a solution is to add:
settagmethod(tag(Dict), "gettable", 

          function(t,k)

            if rawget(Dict,k) then

              return rawget(Dict,k)

            else

              return rawget(t,k)

            end

          end )

which stops the conflict between user data and the dict implementation. --NDT

Sorry, it will not stop the conflict. You just swap the priorities; the conflict is still there. Now 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


See also: PythonLists
RecentChanges · preferences
edit · history
Last edited January 6, 2007 4:06 am GMT (diff)