I'm implementing a lazy-load dictionary; my implementation basically
stores keys that are to be loaded lazily internally (in a member
dictionary) and then in __getitem__ I look to see if the key needs to
be loaded and if so I load it. My class inherits from dict and has
keys(), __contains__, __get/setitem__, items(), etc all properly
implemented--seems to work fine in most cases.
My question is, what methods do I need to implement to make ** work on
a custom dictionary-like object, or is it not possible?
Here's how you can see the problem.
Simplified LazyDict (the real one actually stores callbacks to
generate values, but this is enough to show the problem--my real one
implements a lot more dict methods, but still doesn't work):
class LazyDict(dict):
def __init__(self):
dict.__init__(self)
self.special_keys={}
def addLazy(self, key, val):
if dict.has_key(self, key):
dict.__delitem__(self, key)
self.special_keys[key]=val
def has_key(self, key):
return self.__contains__(key)
def __contains__(self, key):
if dict.has_key(self, key):
return 1
elif dict.has_key(self.special_keys, key):
return 1
else:
def __getitem__(self, key):
if dict.has_key(self, key):
return dict.__getitem__(self, key)
elif key in self.special_keys.keys():
self[key]=self.special_keys[key]
del self.special_keys[key]
return dict.__getitem__(self, key)
return 0
e.g. put the above in LSimple.py and:
>>> def g(foo):
... print foo
...
>>> a=LSimple.LazyDict()
>>> a.addLazy("foo", 2)
>>> g(**a)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: g() takes exactly 1 argument (0 given)
>>> a["foo"]
2
>>> g(**a)
2
>>>
Thanks for your time.
I think you meant to write:
def g(**foo):
print foo
But this seems odd construct. Telling Python to
split a dictionary into separate arguments and then
telling it to reassemble the into a dictionary in
the function. You should consider just passing
the instance of the dictionary and skip all the **
(but this might just be a trivial example).
HTH,
Larry Bates
Syscon, Inc.
"G. S. Hayes" <sjde...@yahoo.com> wrote in message
news:96c2e938.04062...@posting.google.com...
As far as I can tell, it's not possible - Python seems to shortcut the
standard dictionary operators while retrieving the items in the
dictionary (this is possible since dictionaries are built-in, and Python
knows their structure). According to the language reference:
> If the syntax "**expression" appears in the function call, "expression"
> must evaluate to a (subclass of) dictionary
which seems pretty indicative that Python's shortcutting function calls
for efficiency reasons: if you implement every single dictionary method in
a class that's _not_ a subclass of dictionary, **mydict will fail because
Python can't access its values directly.
Your best bet is probably to call the function as func(**dict(mydict)).
dict() doesn't shortcut custom methods, so this should act as expected.
Darn.
> Your best bet is probably to call the function as func(**dict(mydict)).
> dict() doesn't shortcut custom methods, so this should act as expected.
Yeah, I have a .explode() method that tells the dictionary to
de-Lazify everything; that's basically what (**dict)(mydict) would do,
except passing as dict(mydict) would de-Lazify every function call and
.explode() just once (though .explode would keep the exploded values
in memory and dict(mydict) would toss them after the function
returned).
Thanks for your time.
Right, that's what I'm trying to do (hence the Subject).
Turns out that ** bypasses the object methods and grovels internally
in the builtin dict. So I either need to de-Lazify the values in my
dict before I make the ** call or change the code I'm calling to
accept a dictionary (tougher since it's a large body of 3rd-party
code).
Thanks for your time!
>Yeah, I have a .explode() method that tells the dictionary to
>de-Lazify everything; that's basically what (**dict)(mydict) would do,
>except passing as dict(mydict) would de-Lazify every function call and
>.explode() just once (though .explode would keep the exploded values
>in memory and dict(mydict) would toss them after the function
>returned).
You could do something like:
def CallWithLazy(func, lazydict):
if isinstance(func, types.MethodType):
firstarg = 1 # don't want to try to de-Lazify self arg
else:
firstarg = 0
code = func.func_code
for name in code.co_varnames[firstarg:code.co_argcount]:
lazydict[name] #or whatever is necessary to de-lazify
return func(**lazydict)
---
Greg Chapman