pythontwistedgdbm

Dict-like object with no __getitem__, __setitem__


If you do

import gdbm
db = gdbm.open('foo', 'cs')

You get an object which is:

<gdbm.gdbm at 0x7f0982b9aef0>

You can now set keys and values in the database via:

db['foo'] = 'bar'
print db['foo']

I wanted to use these from Twisted and make a wrapper for __getitem__ and __setitem__ which returns deferreds. However, I noticed something peculiar:

In [42]: dir(db)
Out[42]: ['close', 'firstkey', 'has_key', 'keys', 'nextkey', 'reorganize', 'sync']

This object hasn't got __getitem__ and __setitem__. Trying either of these gives an attribute access error. Yet, it behaves like a dictionary. What sort of object is this?

(I suspect this is a C extension object, but I find it odd that it has dict-like access methods, but no __getitem__ and __setitem__ methods. A pointer to Python doc describing this behavior would be helpful.)

Further: how would you get references to db.__getitem__ and db.__setitem__ if you wanted to wrap them in a deferred? The only solution I see is to wrap db in a utility class:

class Db:
    def __init__(self, db):
         self.db = db

    def __getitem__(self, x):
         return self.db[x]

    ...

But perhaps I am missing something obvious?


Solution

  • gdbm is indeed a C module; the object implements the C-API mapping protocol instead.

    See the source code for the PyMappingMethods structure with function pointers.

    You could use operator.getitem() to access the keys:

    >>> from operator import getitem
    >>> db['foo'] = 'bar'
    >>> getitem(db, 'foo')
    'bar'
    

    You could wrap getitem() in functools.partial() to make it an efficient callable with one argument:

    >>> from functools import partial
    >>> gi = partial(getitem, db)
    >>> gi('foo')
    'bar'
    

    As getitem and partial are implemented in C, this combo will always be faster than custom objects, functions or lambdas to wrap the access.