Re: [Python-ideas] [Python-Dev] hello, new dict addition for new eve ?

Hello, I had tough time finding a connection to internet :) @Arnaud + Terry Thx, code corrected to have the right terminology. at least my code really test for the good rules :) But being precise matter, so that I changed all the terms to fit in. @nathan << is a symbol indicating an asymetry for left and right value, and I carefully tried to have a symetrical behaviour with left & right : associativity, distribution, commutativity. The whole point about using a symbol, is that it follow rules. That is the reason why consistent_addition class is all about testing linear algebraic rules (hopefully the right way, and now, I hope with the right terminology). But I have reason to think that there are more than way to do addition. Linear algebrae is the most used and intuitive one for non developer, and developer think of sets operations as more intuitive. I guess, it all come down to one point. Which is what is a dict as a mathematical object. If there is more than one choice, which has the most benefits. Should there be more than one definition ? @Guido & Eric In my book there are rules for sets. The question is a dict the same as a vector or as a set ? since sets are using logical operators for operations that are roughly sets operations why not use & ^ | for sets operation on dict ? it would be pretty consistent with sets operations. ( @jakkob ) In this case I would expect inconsistencies error when doing dict( a = 1 ) | dict( a = 2 , b = 2 ) (thus key-> value are seen like an atomic element of the set, unless a "value collision" operator is given, and this collision operator would recursively and naturely apply to the descendants) because I expect both a | b == b | a and a + b = b + a The problem would be indeed list + list vectorish behaviour is my prefered of course. but if we shift the record algebrae addition to another symbol (let's assume << ) we resolve the conflict. But then we have a problem of retro compatibility. We then may be tempted to use & | ^ (since it would be used for dict) and then we have a problem : should [ 1 , 2 ] & [ 3 , 2 ] mean set addition or applying & to all the element of the same rank ? I'm pretty stucked myself in trying to be consistent. Just for the record, why dit you not use "." (dot) for concatenation ? I know it is typographically unreadable on a messy screen, but is there a better reason ? @terry & Nathan Regarding Counter. on https://github.com/jul/ADictAdd_iction/blob/master/demonstrate.py#L120 you may notice that I do have all the property of counter, but counter does not as -this dict- aggregate values and (sub) totals at the same time. It is very convenient for map reduce since it does some re-reduce operations at reduce time. smart, no ? <:o) @terry Sorry for making assertive and carefree statements regarding strong resentment. My bad. @bruce, truth is aim at similarity cosinus for dict, and I imagine a map reduce on dict representing values of a model you want ideal = dict( blue = 1 , height = 180, weight = 70, wage = 500 ) in the filter before the map reduce you would want to filter all records close to this one by using similarity cosinus in this way : filter( lambda model : cos( ideal, model) > .7 , all_model ) http://en.wikipedia.org/wiki/Cosine_similarity of course I will try to advocate dot product, and metrics. As a result my real goal is to make people consider dict as vectors of path to value => value not sets of key => value To solve issues such as weighted decisions and non linear choices (based on trigger or a value belonging to a set) I can fairly easily concevie a projector (which would be a class transforming a vector in a vector, but not with a a matrix, but with computed rules ). @Mark Jansen +1 are you telepath ? Yes a key collision operator would be nice : given for instance an apache log, I will have segments of path, and I may want keys to be the accumulated graph of a user on a website. so my collision rule might be : (key being the referer value the page visited afterwards) dict( a = b, a = c ) + dict( b = e ) = dict( a = dict( b = e ), a = c) I thought of it, but I really loved the conservation rule. And I feared people would think of it as too complicated. Keep It Simple I learnt :) @all Wrapping everything to logicial sense. I was thinking of supersets of object (thus out of the stdlib) that would have different algebrae and could be used as casts on object to redifine + - * / cos & | ^ and for different objects would behave consistently ex : vector( dict ) vector( list ) vector( string ) would make dict & list & string behave like vectors thus mainly supporting elementwise operations sets( dict ) , sets( string ) ... would make dict string be sets of elements ... (with the original dict addition design of GvR et al) Each superset would have a unit test for the operation verifying that behaviour is consistent. (associativity, distributivity, ...) what about this solution ? -- Friendly jul

On 12/31/2011 11:07 AM, julien tayon wrote:
The question is a dict the same as a vector or as a set ?
A dict is a mapping, which is to say, a functional set of key,value pairs. 'Functional' means that the keys are unique. This is a key property (pun intended). A vector can be regarded as a functional set of count,value pairs. Some languages use dicts for vectors. Even in Python, people use dicts for sparse arrays of various dimensions, with unspecified pairs assumed to have a 0 value. So it makes the most sense to me for d1 + d2 to be interpreted as set union, with some resolution of key conflicts to restore the functional property. Your vector interpretation of adding values only works for values that can be added.
since sets are using logical operators for operations that are roughly sets operations why not use & ^ | for sets operation on dict ? it would be pretty consistent with sets operations.
I agree, except that dicts are not just sets of pairs, but functional sets. If one wants to view a dict simply as a set of keys, values, or pairs, ignoring the functional property for the pairs, that is what the .keys(), .values(), and .items() set view methods are for. All the set operations are available on the resulting set view objects. -- Terry Jan Reedy

On Sat, Dec 31, 2011 at 4:55 PM, Terry Reedy <tjreedy@udel.edu> wrote:
On 12/31/2011 11:07 AM, julien tayon wrote:
The question is a dict the same as a vector or as a set ?
A dict is a mapping, which is to say, a functional set of key,value pairs. 'Functional' means that the keys are unique. This is a key property (pun intended). A vector can be regarded as a functional set of count,value pairs. Some languages use dicts for vectors. Even in Python, people use dicts for sparse arrays of various dimensions, with unspecified pairs assumed to have a 0 value.
So it makes the most sense to me for d1 + d2 to be interpreted as set union, with some resolution of key conflicts to restore the functional property. Your vector interpretation of adding values only works for values that can be added.
It occurs to me that any argument for a vector interpretation of dicts, with element-wise operations, should apply equally well to lists and tuples, which are perhaps even more natural as representations for vectors. But of course, the + operator for sequences is interpreted as concatenation, not element-wise addition; it would be strange if the reverse were true for dicts. The necessary resolution of key conflicts is what makes "+" feel less natural to me for dicts than it does for sequences, where the order of the operands is transparently reflected in the output. For dicts, in contrast, we are talking about a "lossy" union/combination where what is lost depends on the order of the operands—this is less transparent in the output. However it is spelled, though, I would be thrilled if such an operation were built in. :) Nathan
since sets are using logical operators for operations that are roughly sets operations why not use & ^ | for sets operation on dict ? it would be pretty consistent with sets operations.
I agree, except that dicts are not just sets of pairs, but functional sets. If one wants to view a dict simply as a set of keys, values, or pairs, ignoring the functional property for the pairs, that is what the .keys(), .values(), and .items() set view methods are for. All the set operations are available on the resulting set view objects.
-- Terry Jan Reedy
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

On 12/31/2011 5:57 PM, Nathan Schneider wrote:
The necessary resolution of key conflicts is what makes "+" feel less natural to me for dicts than it does for sequences, where the order of the operands is transparently reflected in the output. For dicts, in contrast, we are talking about a "lossy" union/combination where what is lost depends on the order of the operands—this is less transparent in the output. However it is spelled, though, I would be thrilled if such an operation were built in. :)
You mean like dict.update? -- Terry Jan Reedy

On Sun, Jan 1, 2012 at 3:24 AM, Terry Reedy <tjreedy@udel.edu> wrote:
You mean like dict.update?
Nah nah, that's "+= "; "+" is dict(d1, **d2). -- Devin On Sun, Jan 1, 2012 at 3:24 AM, Terry Reedy <tjreedy@udel.edu> wrote:
On 12/31/2011 5:57 PM, Nathan Schneider wrote:
The necessary resolution of key conflicts is what makes "+" feel less natural to me for dicts than it does for sequences, where the order of the operands is transparently reflected in the output. For dicts, in contrast, we are talking about a "lossy" union/combination where what is lost depends on the order of the operands—this is less transparent in the output. However it is spelled, though, I would be thrilled if such an operation were built in. :)
You mean like dict.update?
-- Terry Jan Reedy
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

2011/12/31 Nathan Schneider <nathan@cmu.edu>:
On Sat, Dec 31, 2011 at 4:55 PM, Terry Reedy <tjreedy@udel.edu> wrote:
On 12/31/2011 11:07 AM, julien tayon wrote:
The question is a dict the same as a vector or as a set ?
A dict is a mapping, which is to say, a functional set of key,value pairs. 'Functional' means that the keys are unique. This is a key property (pun intended). A vector can be regarded as a functional set of count,value pairs. Some languages use dicts for vectors. Even in Python, people use dicts for sparse arrays of various dimensions, with unspecified pairs assumed to have a 0 value. Yes, a potentialy indefinite sparse value vector on a base at least greater. For which the dimension is the path of keys to a value. And I lamely admitted these were orthogonal in my mind to one another. (therefore dicthing coupling problematics in the first time since I will think of matrix later)
That is the reason, I intend to back down on any argumentation on vectoriel meaning of dict without proper code to back up my claims :) .
It occurs to me that any argument for a vector interpretation of dicts, with element-wise operations, should apply equally well to lists and tuples, which are perhaps even more natural as representations for vectors. But of course, the + operator for sequences is interpreted as concatenation, not element-wise addition; it would be strange if the reverse were true for dicts.
I do admit I came to the fact you are right. And, I think that if two keys were equals but not the value it should raise a collision exception. I could provide a consistent "set addition" for dict as an amend for making so much noise :) I would also provide sub. I see a way to provide a dynamic dict to sets translator that would be quite weiredl.
The necessary resolution of key conflicts is what makes "+" feel less natural to me for dicts than it does for sequences, where the order of the operands is transparently reflected in the output. For dicts, in contrast, we are talking about a "lossy" union/combination where what is lost depends on the order of the operands—this is less transparent in the output. However it is spelled, though, I would be thrilled if such an operation were built in. :)
I can write my module for this. :) (I need to document myself on magic methods) Well, if dict are vectors I must define : - matrix (projection of vectors in an another base) - distance (cosine similarity) I will therefore be able to define the notion of proximity once done that will tell if something is almost another thing a word counter of a text should be close to the word counters of the keyword it triggers. PS I did not quite catch the functionnal thingy. Cheers -- jul

Here are some "utility" functions I had to write in my current project: # add entries from d2 to d1 # supports only dict and numeric types for now # recursive, applies function to all subdicts def addDictEntries(d1, d2) # take the max of entries from d2 to d1 # supports only dict and numeric types for now # recursive, applies function to all subdicts def maxDictEntries(d1, d2): # take the min of entries from d2 to d1 # supports only dict and numeric types for now # recursive, applies function to all subdicts def minDictEntries(d1, d2) # merges entries from d2 to d1 like update does # if an entry already exists and the values differ then throws an exception # recursive, applies function to all subdicts def mergeDictEntriesSafe(d1, d2) # traverses through the dict and converts all defaultdicts to dicts # this is done so that missing elements will throw an exception rather than creating a default entry # also, defaultdicts aren't picklable so this is required in order to serialize a dict def unDefaultDict(d) # same as unDefaultDict but isn't recursive. Only applies to top level keys def unDefaultDictShallow(d) The usage for all this varies but I found that combining / adding dicts together becomes very prevalent when you split datasets up for multihreading/multiprocessing purposes and need to recombine it later. so I propose that a "combine" function is added to the dict class and all dict subclasses. The function would have the following arguments: def combine(self, d2, conflictFunc=None): If combineFunc is a function, then that function is called and the result is used. val = conflictFunc(k1,k2); If combineFunc is a string, then self's key value calls conflictFunc and the new value is the return value def combine(self, d2, combineFunc=None): if combineFunc == None: for k,i in d2.items(): if(not k in self): self[k] = copy.deepcopy(i); else: if(hasattr(i,'keys')): self[k].combine(i); else: if self[k] != i: raise someUsefulException(); return; if (type(combineFunc) == str): for k,i in d2.items(): if(not k in self): self[k] = copy.deepcopy(i); else: if(hasattr(i,'keys')): self[k].combine(i,combineFunc); else: self[k] = getattr(self[k],combineFunc)(i); else: for k,i in d2.items(): if(not k in self): self[k] = copy.deepcopy(i); else: if(hasattr(i,'keys')): self[k].combine(i,combineFunc); else: self[k] = combineFunc(self[k],i); examples: d.combine(d2); # update with d2 but throw an exception if any keys have different values d.combine(d2, myCustomFunc); # if conflict, then result is = myCustomFunc(v1,v2) which would be defined elsewhere d.combine(d2, '__add__'); # if conflict, then result is = v1 + v2
print(d1) {'a': 'b', 'c': {'g': 5}} print(d2) {'a': 'ecause', 'c': {'g': 10}} d1.combine(d2,'__add__'); print(d1) {'a': 'because', 'c': {'g': 15}}
I think doing so would be more powerful and less ambiguous than trying to give dict a '+' A second function I suggest adding is asDict(). This would return a dictionary with all sub dictionaries-types converted to the dict type. I.E. all defaultdicts and counters become just plain dicts. This would be the same as my unDefaultDict function. It could take an optional argument to do the conversion only to the shallow keys def asDict(self, shallow=False)

It's funny, I joined this list several days ago to ask about this very problem. Perhaps I can add something (there's code below if you want to skip the discussion): I do research in computational linguistics, and adding/incrementing dictionary values is a very common task (e.g. counting unique words in a corpus). A defaultdict and a for loop have served me well toward this end (and Counter may be nice, but I haven't tried it), but I've always wanted something more functional. I found in Haskell a nice method Map.fromListWith that takes a list of (key, value) pairs and a function describing how to deal with collisions. It's useful and flexible. Summing up what has been said, as I understand it, it's not obvious what a good behaviour for + is with dicts, and we can't assure it follows algebraic properties like commutativity. As Guido hinted at in the first messages, addition over disjoint dicts (and we can include the identity dict) should be the same no matter the operation. That is: {k1:v1} + {k2:v2} = {k1:v1, k2:v2} {k1:v1} + {} = {k1:v1} The only interesting cases are when we have the same key: {k1:v1} + {k1:v2} = {k1:op(v1,v2)} where op is some binary function. While dict doesn't have __add__ defined, it does resolve conflicts by just using the latter value (i.e. op=lambda x, y: y), as in the following three statements: d = dict([(k1,v1), (k1,v2)]) # result: {k1:v2} d[k1] = v3 # result: {k1:v3} d.update([(k1,v4) # result: {k1:v4} I think the latter two, at least, should not be counted as addition, but rather stay as assignment. Someone brought up the idea of a __collision__ method that is called when __setitem__ is called on an existing key. I like this idea, and there's probably some use for it, but for implementing addition it might not be practical (how, then, would you re-assign a key's value without forcing "op" to apply on the new and existing value?). I propose something like the following proof-of-concept: class AccumulationDict(dict): def __init__(self, accumulator, *args, **kwargs): if not callable(accumulator): raise TypeError('Accumulator must be callable.') self.accumulator = accumulator self.accumulate(*args, **kwargs) def __additem__(self, key, value): if key in self: self[key] = self.accumulator(self[key], value) else: self[key] = value def __add__(self, other): result = AccumulationDict(self.accumulator, self) # check if other's accumulator is same? result.accumulate(other) return result def accumulate(self, *args, **kwargs): for arg in args: if isinstance(arg, list): for (key, value) in arg: self.__additem__(key, value) elif isinstance(arg, dict): for (key, value) in arg.items(): self.__additem__(key, value) else: raise TypeError('Argument must be of type list or dict.') for key in kwargs: self.__additem__(key, kwargs[key]) Which has the following properties: * takes a binary function for accumulating existing and new values * otherwise can be instantiated like a dict * accumulate() function is like update(), but uses the accumulator to deal with conflicts * KeyErrors still occur when getting a non-existent key * addition is supported and returns a new object with the merged dicts * __setitem__ and update() are unchanged so they can be used to assign values
d = AccumulationDict(operator.add, [('a',1),('b',1),('a',1)], b=1) d {'a': 2, 'b': 2} d['a'] = 0 d {'a': 0, 'b': 2} d + {'a':3,'b':1} {'a': 3, 'b': 3} d {'a': 0, 'b': 2} d['c'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'c' d.update({'c':1}) d {'a': 0, 'c': 1, 'b': 2} d.accumulate({'c':1}) d {'a': 0, 'c': 2, 'b': 2}
I hope this contributes to the discussion, and I'd be happy to hear your thoughts. Thanks, -- -Michael Wayne Goodman

This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items()) < set(dict2.items()) many times. I don't even know what rich comparison operators on dictionaries do now, it isn't intuitive at all. Nathan

On 1/3/12 4:59 PM, Nathan Rice wrote:
This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items())< set(dict2.items()) many times.
The values are unrestricted Python objects. They do not have to be hashable or sortable. The set operations you describe would have to be require one or both (or else do something algorithmically horrendous). Further, you cannot treat dicts as sets of (key, value) pairs because dicts have unique keys, not unique (key, value) pairs.
I don't even know what rich comparison operators on dictionaries do now, it isn't intuitive at all.
The rich comparison operators are only defined for == and !=. In Python 2.x, there is a legacy implementation of __cmp__ that does something more complicated. I recommend ignoring it. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco

On Tue, Jan 3, 2012 at 12:10 PM, Robert Kern <robert.kern@gmail.com> wrote:
On 1/3/12 4:59 PM, Nathan Rice wrote:
This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items())< set(dict2.items()) many times.
The values are unrestricted Python objects. They do not have to be hashable or sortable. The set operations you describe would have to be require one or both (or else do something algorithmically horrendous).
I haven't had any problems with using set(somedict.items()). I will admit that I primarily do this in simple contexts. This brings me to another curiosity... Why do mutable items not implement hash using id()?
Further, you cannot treat dicts as sets of (key, value) pairs because dicts have unique keys, not unique (key, value) pairs.
I'm confused. Because keys are unique, (key, value) pairs are unique as well. I realize the values are not unique but the tuple is guaranteed to be.
I don't even know what rich comparison operators on dictionaries do now, it isn't intuitive at all.
The rich comparison operators are only defined for == and !=. In Python 2.x, there is a legacy implementation of __cmp__ that does something more complicated. I recommend ignoring it.
Recommendation definitely noted :D Nathan

On 1/3/12 5:39 PM, Nathan Rice wrote:
On Tue, Jan 3, 2012 at 12:10 PM, Robert Kern<robert.kern@gmail.com> wrote:
On 1/3/12 4:59 PM, Nathan Rice wrote:
This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items())< set(dict2.items()) many times.
The values are unrestricted Python objects. They do not have to be hashable or sortable. The set operations you describe would have to be require one or both (or else do something algorithmically horrendous).
I haven't had any problems with using set(somedict.items()). I will admit that I primarily do this in simple contexts.
This brings me to another curiosity... Why do mutable items not implement hash using id()?
Usually because they do not implement __eq__ using id(). The invariant that needs to be maintained is that if two objects compare equal, then they need to hash equal. Many mutable objects compare by value, and thus two non-identical objects can compare equal.
Further, you cannot treat dicts as sets of (key, value) pairs because dicts have unique keys, not unique (key, value) pairs.
I'm confused. Because keys are unique, (key, value) pairs are unique as well. I realize the values are not unique but the tuple is guaranteed to be.
Yes, the dict.items() would be a valid, unique set, but set operations on those sets may not give valid dicts because the same key could point to different values in the two dict operands. For example: [~] |7> a = {'one': 1} [~] |8> b = {'one': '1'} [~] |9> set(a.items()) | set(b.items()) set([('one', '1'), ('one', 1)]) [~] |10> dict(set(a.items()) | set(b.items())) {'one': 1} You've lost a value there. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco

On 3 January 2012 18:45, Robert Kern <robert.kern@gmail.com> wrote:
[~] |7> a = {'one': 1}
[~] |8> b = {'one': '1'}
[~] |9> set(a.items()) | set(b.items()) set([('one', '1'), ('one', 1)])
[~] |10> dict(set(a.items()) | set(b.items())) {'one': 1}
I don't know what you've done here:
a = {'one': 1} b = {'one': 1} set(a.items()) | set(b.items()) {('one', 1)} dict(set(a.items()) | set(b.items())) {'one': 1}
( However, your point remains:
a = {'number': 1} b = {'number': 2} set(a.items()) | set(b.items()) {('number', 1), ('number', 2)} dict(set(a.items()) | set(b.items())) {'number': 2}
)

On 1/3/12 6:55 PM, Joshua Landau wrote:
On 3 January 2012 18:45, Robert Kern <robert.kern@gmail.com <mailto:robert.kern@gmail.com>> wrote:
[~] |7> a = {'one': 1}
[~] |8> b = {'one': '1'}
[~] |9> set(a.items()) | set(b.items()) set([('one', '1'), ('one', 1)])
[~] |10> dict(set(a.items()) | set(b.items())) {'one': 1}
I don't know what you've done here:
a = {'one': 1} b = {'one': 1} set(a.items()) | set(b.items()) {('one', 1)} dict(set(a.items()) | set(b.items())) {'one': 1}
Sorry, I was a little too clever for my own good: a = {'one': 1} b = {'one': '1'} The first has the integer 1; the second has the string '1'. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco

On Tue, Jan 3, 2012 at 1:45 PM, Robert Kern <robert.kern@gmail.com> wrote:
On 1/3/12 5:39 PM, Nathan Rice wrote:
I haven't had any problems with using set(somedict.items()). I will admit that I primarily do this in simple contexts.
This brings me to another curiosity... Why do mutable items not implement hash using id()?
Usually because they do not implement __eq__ using id(). The invariant that needs to be maintained is that if two objects compare equal, then they need to hash equal. Many mutable objects compare by value, and thus two non-identical objects can compare equal.
Ok. I missed that invariant in the data model (and a couple of other places, apparently). Thanks.
Further, you cannot treat dicts as sets of (key, value) pairs because dicts have unique keys, not unique (key, value) pairs.
I'm confused. Because keys are unique, (key, value) pairs are unique as well. I realize the values are not unique but the tuple is guaranteed to be.
Yes, the dict.items() would be a valid, unique set, but set operations on those sets may not give valid dicts because the same key could point to different values in the two dict operands. For example:
[~] |7> a = {'one': 1}
[~] |8> b = {'one': '1'}
[~] |9> set(a.items()) | set(b.items()) set([('one', '1'), ('one', 1)])
[~] |10> dict(set(a.items()) | set(b.items())) {'one': 1}
You've lost a value there.
I have been following the earlier discussion of + in the context of dictionaries, and I agree that many operators do not cleanly map over. It seems to me that the comparison operators are well behaved in this regard though. As an additional aside, perhaps the return type of elements in the items call could be made into a nice "Item" namedtuple. I always unpack my items into (k, v) or something similar when possible, but some people aren't as considerate. Having item.key and item.value available (and used in examples) encourages nice code; even if someone chose a horrible name for their items() unpacking, it is easier to grok "badname.key ... badname.value" than "badname[0] ... badname[1]". Nathan

On 1/3/2012 1:45 PM, Robert Kern wrote:
|7> a = {'one': 1} |8> b = {'one': '1'} |9> set(a.items()) | set(b.items()) set([('one', '1'), ('one', 1)])
In Python 3, the subject of this list, converting set views to sets is redundant.
a.items() | b.items() {('one', '1'), ('one', 1)} set(a.items()) | set(b.items()) {('one', '1'), ('one', 1)}
-- Terry Jan Reedy

The values are unrestricted Python objects. They do not have to be hashable or sortable. The set operations you describe would have to be require one or both (or else do something algorithmically horrendous).
He only describes <, which can be implemented in linear time as: def __lt__(self, d2): if not isinstance(d2, dict): return NotImplemented return all(key in d2 and d2[key] == value for key, value in self.items()) Which set operations are you thinking of? -- Devin On Tue, Jan 3, 2012 at 12:10 PM, Robert Kern <robert.kern@gmail.com> wrote:
On 1/3/12 4:59 PM, Nathan Rice wrote:
This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items())< set(dict2.items()) many times.
The values are unrestricted Python objects. They do not have to be hashable or sortable. The set operations you describe would have to be require one or both (or else do something algorithmically horrendous).
Further, you cannot treat dicts as sets of (key, value) pairs because dicts have unique keys, not unique (key, value) pairs.
I don't even know what rich comparison operators on dictionaries do now, it isn't intuitive at all.
The rich comparison operators are only defined for == and !=. In Python 2.x, there is a legacy implementation of __cmp__ that does something more complicated. I recommend ignoring it.
-- Robert Kern
"I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

On 1/3/12 8:50 PM, Devin Jeanpierre wrote:
The values are unrestricted Python objects. They do not have to be hashable or sortable. The set operations you describe would have to be require one or both (or else do something algorithmically horrendous).
He only describes<, which can be implemented in linear time as:
def __lt__(self, d2): if not isinstance(d2, dict): return NotImplemented
return all(key in d2 and d2[key] == value for key, value in self.items())
Actually, this implements __le__. For __lt__, you need to exclude the case where they are exactly equal. You're right though that it wouldn't be algorithmically horrendous to do this. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco

On 1/3/2012 11:59 AM, Nathan Rice wrote:
This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items())< set(dict2.items()) many times.
As a said earlier in this thread, being able to view dicts as sets is such a good idea that it was already added a few years ago.
{1:1}.items() < {1:1, 2:2}.items() True
I don't even know what rich comparison operators on dictionaries do now, it isn't intuitive at all.
TypeError: unorderable types: dict() < dict() -- Terry Jan Reedy

On Tue, Jan 3, 2012 at 4:02 PM, Terry Reedy <tjreedy@udel.edu> wrote:
On 1/3/2012 11:59 AM, Nathan Rice wrote:
This is slightly tangential, but I've always wondered... Why aren't set operations implemented on dicts? It is fairly natural to view a dictionary as a set of (key, value) pairs. Things like subset/superset checking (with concomitant operator support) make sense. I have written stuff like set(dict1.items())< set(dict2.items()) many times.
As a said earlier in this thread, being able to view dicts as sets is such a good idea that it was already added a few years ago.
{1:1}.items() < {1:1, 2:2}.items() True
Yes, my apologies, I knew about views but I missed the part where they have set operations. I was under the impression when I wrote that they were just plain iterators. I try to keep current on Py3 nuances, but since my work ties me to Py2, I will miss subtle but important points like that from time to time. I still don't think there is anything wrong with having the comparison operations present on the dictionary directly, it seems intuitive *to me*. Of course I understand that I may be an outlier in this (and it certainly wouldn't be the first time :P). Nathan

2012/1/3 Nathan Rice <nathan.alexander.rice@gmail.com>:
As a said earlier in this thread, being able to view dicts as sets is such a good idea that it was already added a few years ago.
{1:1}.items() < {1:1, 2:2}.items() True
Making me think of funny stuffs :
dict( a = .1 * .1 ) > dict( a = .01 ) True ( value compared, floats are behaving as they always do)
dict( a = lambda x : x ) == dict( a = lambda x : x ) False ( value compared, and lambda are id compared I guess)
And since I always mixed up truth and lies :
False , True = True, False == True and "to be" or not "to be" if "logic" is "insane" else 42 -- Jul
participants (9)
-
Devin Jeanpierre
-
goodman.m.w@gmail.com
-
James Hutchison
-
Joshua Landau
-
julien tayon
-
Nathan Rice
-
Nathan Schneider
-
Robert Kern
-
Terry Reedy