Case-insensitive comparison of sets in Python

后端 未结 4 1035

I have two sets (although I can do lists, or whatever):

a = frozenset((\'Today\',\'I\',\'am\',\'fine\'))
b = frozenset((\'hello\',\'how\',\'are\',\'you\',\'t         


        
4条回答
  •  -上瘾入骨i
    2021-01-19 15:58

    Unfortunately, even if you COULD "change on the fly" the comparison-related special methods of the sets' items (__lt__ and friends -- actually, only __eq__ needed the way sets are currently implemented, but that's an implementatio detail) -- and you can't, because they belong to a built-in type, str -- that wouldn't suffice, because __hash__ is also crucial and by the time you want to do your intersection it's already been applied, putting the sets' items in different hash buckets from where they'd need to end up to make intersection work the way you want (i.e., no guarantee that 'Today' and 'today' are in the same bucket).

    So, for your purposes, you inevitably need to build new data structures -- if you consider it "inelegant" to have to do that at all, you're plain out of luck: built-in sets just don't carry around the HUGE baggage and overhead that would be needed to allow people to change comparison and hashing functions, which would bloat things by 10 times (or more) for the sae of a need felt in (maybe) one use case in a million.

    If you have frequent needs connected with case-insensitive comparison, you should consider subclassing or wrapping str (overriding comparison and hashing) to provide a "case insensitive str" type cistr -- and then, of course, make sure than only instances of cistr are (e.g.) added to your sets (&c) of interest (either by subclassing set &c, or simply by paying care). To give an oversimplified example...:

    class ci(str):
      def __hash__(self):
        return hash(self.lower())
      def __eq__(self, other):
        return self.lower() == other.lower()
    
    class cifrozenset(frozenset):
      def __new__(cls, seq=()):
        return frozenset((ci(x) for x in seq))
    
    a = cifrozenset(('Today','I','am','fine'))
    b = cifrozenset(('hello','how','are','you','today'))
    
    print a.intersection(b)
    

    this does emit frozenset(['Today']), as per your expressed desire. Of course, in real life you'd probably want to do MUCH more overriding (for example...: the way I have things here, any operation on a cifrozenset returns a plain frozenset, losing the precious case independence special feature -- you'd probably want to ensure that a cifrozenset is returned each time instead, and, while quite feasible, that's NOT trivial).

提交回复
热议问题