From: kkumer on 7 Jun 2010 16:17 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)
From: Thomas Jollans on 7 Jun 2010 16:33 On 06/07/2010 10:17 PM, kkumer wrote: > 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 **. > My guess would be that this only works with dicts, and uses the internal representation of the dict, not the python-defined methods. I don't know what you want to do, but you might be better off creating a special "mergeabledict" type for the parents, and then allow them to connect, actually copying all items over. Though it might be best just to use one single dict ;-) > 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) > >
From: Ian Kelly on 7 Jun 2010 17:04 On Mon, Jun 7, 2010 at 2:17 PM, kkumer <kkumer(a)best-search-engines-mail.com> wrote: > > 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 don't think that what you want to do here is possible. It appears to be hard-coded and not affected by subclassing, as evidenced by the following snippet: class NonDict(dict): for attr in dict.__dict__: if attr not in ('__init__', '__new__',): locals()[attr] = None d = NonDict({'a': 1}) def kwa(**kw): print kw kwa(**d) Prints: {'a': 1} Cheers, Ian
From: Dave Angel on 7 Jun 2010 18:01 kkumer wrote: > 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) > > > From the help on "Container types", >>The UserDict <../library/userdict.html#module-UserDict> module provides a DictMixin class to help create those methods from a base set of __getitem__() <#object.__getitem__>, __setitem__() <#object.__setitem__>, __delitem__() <#object.__delitem__>, and keys(). << and >>It is recommended that both mappings and sequences implement the __contains__() <#object.__contains__> method to allow efficient use of the in operator; for mappings, in should be equivalent of has_key(); for sequences, it should search through the values. It is further recommended that both mappings and sequences implement the __iter__() <#object.__iter__> method to allow efficient iteration through the container; for mappings, __iter__() <#object.__iter__> should be the same as iterkeys(); for sequences, it should iterate through the values. << I'd guess that the ** operator calls one or more of these special methods directly, rather than calling the equivalent regular methods. DaveA
From: Peter Otten on 7 Jun 2010 18:03 kkumer wrote: > > 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 **. The following experiment shows that you only need to implement a keys() and __getitem__() method. $ cat kw.py class A(object): def keys(self): return list("ab") def __getitem__(self, key): return 42 def f(**kw): print(kw) f(**A()) $ python kw.py {'a': 42, 'b': 42} However, if you have A inherit from dict... $ cat kwd.py class A(dict): def keys(self): return list("ab") def __getitem__(self, key): return 42 def f(**kw): print(kw) f(**A()) $ python kwd.py {} 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. Peter
|
Next
|
Last
Pages: 1 2 3 4 Prev: Python + vim + spaces vs tab Next: PyCon Australia 2010 registration deadline reminder |