Which objects are expanded by double-star ** operator?

2010-06-07 Thread kkumer

I have to merge two dictionaries into one, and in
a "shallow" way: changing items should be possible
by operating either on two parents or on a
new dictionary. I am open to suggestions how
to do this (values are always numbers, BTW), but
I tried to do it by creating a dict-like class that just
forwards all calls to the two parent dicts, see below.

It works, but one important thing is missing. I
am not able to expand new dictionary with 
double-star operator ** to use it as a
set of keyword arguments of a function. 
I googled a bit, but was unable to find what
property must an object have to be correctly
treated by **.

I hope the following code is self-explanatory:



import itertools

class hubDict(dict):
"""Merges two dictionaries, but not actually but just by forwarding."""

def __init__(self, da, db):
self.d1 = da
self.d2 = db

def __getitem__(self, name):
if self.d1.has_key(name):
return self.d1[name]
else:
return self.d2[name]

def __setitem__(self, name, value):
if self.d1.has_key(name):
self.d1[name] = value
else:
self.d2[name] = value

def __iter__(self):
return itertools.chain(self.d1.__iter__(), self.d2.__iter__())

def has_key(self, name):
if self.d1.has_key(name) or self.d2.has_key(name):
return True
else:
return False

def keys(self):
return self.d1.keys() + self.d2.keys()

def items(self):
return self.d1.items() + self.d2.items()

def iteritems(self):
return itertools.chain(self.d1.iteritems(), self.d2.iteritems())

def iterkeys(self):
return itertools.chain(self.d1.iterkeys(), self.d2.iterkeys())

def itervalues(self):
return itertools.chain(self.d1.itervalues(), self.d2.itervalues())

def copy(self):
print "Can't copy hubDict yet!!!"

def update(self, d):
for key in d:
self.__setitem__(key, d[key])

def popitem(self):
try:
return self.d1.popitem()
except KeyError:
return self.d2.popitem()

def __repr__(self):
return 'First: %s\nSecond: %s' % (
self.d1.__repr__(), self.d2.__repr__())


# Trying it now:

da = {'a':1}
db = {'b':2}
dh = hubDict(da, db)
def kwa(**kwargs): print kwargs

#OK
kwa(**da)

#not OK: prints empty dict
kwa(**dh)


import itertools

class hubDict(dict):
"""Merges two dictionaries, but not actually but just by forwarding."""

def __init__(self, da, db):
self.d1 = da
self.d2 = db

def __getitem__(self, name):
if self.d1.has_key(name):
return self.d1[name]
else:
return self.d2[name]

def __setitem__(self, name, value):
if self.d1.has_key(name):
self.d1[name] = value
else:
self.d2[name] = value

def __iter__(self):
return itertools.chain(self.d1.__iter__(), self.d2.__iter__())

def has_key(self, name):
if self.d1.has_key(name) or self.d2.has_key(name):
return True
else:
return False

def keys(self):
return self.d1.keys() + self.d2.keys()

def items(self):
return self.d1.items() + self.d2.items()

def iteritems(self):
return itertools.chain(self.d1.iteritems(), self.d2.iteritems())

def iterkeys(self):
return itertools.chain(self.d1.iterkeys(), self.d2.iterkeys())

def itervalues(self):
return itertools.chain(self.d1.itervalues(), self.d2.itervalues())

def copy(self):
print "Can't copy hubDict yet!!!"

def update(self, d):
for key in d:
self.__setitem__(key, d[key])

def popitem(self):
try:
return self.d1.popitem()
except KeyError:
return self.d2.popitem()

def __repr__(self):
return 'First: %s\nSecond: %s' % (
self.d1.__repr__(), self.d2.__repr__())

def __len__(self):
return self.d1.__len__() + self.d2.__len__()

# Trying it now:

da = {'a':1}
db = {'b':2}
dh = hubDict(da, db)
def kwa(**kwargs): print kwargs

#OK
kwa(**da)

#not OK: prints empty dict
kwa(**dh)

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Which objects are expanded by double-star ** operator?

2010-06-08 Thread kkumer
(sorry for posting empty post by accident)

Peter Otten <[email protected]> wrote:
> it stops working -- probably a side-effect of some optimization.
> So if you change your hubDict's base class from dict to object you should 
> get the desired behaviour.

Yes, as already noted, this would require python >=1.6, and I am
reluctant to upgrade at this point.

What I ended up doing is to simply create a normal dictionary
and copy all items from my hubDict merged "dictionary" into it
just for the purpose of this one function call

auxdict = dict((it for it in hubDictinstance.items()))
f(**auxdict)

Since this happens only few times during runtime it's not a big deal.

Thanks to all for insights.

K.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Which objects are expanded by double-star ** operator?

2010-06-08 Thread kkumer
Bryan  wrote:
> I get the same bug-like behavior in 3.1. I think Peter is right that
> it's probably a side-effect of an optimization. kkumer seems to have
> completely over-ridden the methods of dict, but if we insert into his
> hubDict with the parent class's method:
> 
>  dict.__setitem__(dh, 'c', 3)
> 
> Then the **dh argument passes the keyword arg c=3.

Yes. But problem is that this setting of item
should also affect one of the two original parent dicts,
while  your proposal affects only hubDict instance.
(hubDict is never used to create new items, just to change
values of existing ones, which belong to one of
two parents). 

K.
-- 
http://mail.python.org/mailman/listinfo/python-list