Python中不区分大小写的集合比较

Python中不区分大小写的集合比较,python,django,compare,Python,Django,Compare,我有两套(尽管我可以做列表或其他什么): 我想得到: frozenset(['Today']) 或者至少: frozenset(['today']) 第二种选择是可行的,如果我把所有我认为的都小写,但我正在寻找一种更优雅的方式。有可能吗 a.intersection(b) 以不区分大小写的方式 Django中的快捷方式也很好,因为我使用的是这个框架 下面的交叉点方法示例(我不知道如何在注释中格式化): 首先,你的意思是不是a.交叉点(b)?交叉点(如果不区分大小写)将设置为set(['t

我有两套(尽管我可以做列表或其他什么):

我想得到:

frozenset(['Today'])
或者至少:

frozenset(['today'])
第二种选择是可行的,如果我把所有我认为的都小写,但我正在寻找一种更优雅的方式。有可能吗

a.intersection(b) 
以不区分大小写的方式

Django中的快捷方式也很好,因为我使用的是这个框架

下面的交叉点方法示例(我不知道如何在注释中格式化):


首先,你的意思是不是
a.交叉点(b)
?交叉点(如果不区分大小写)将设置为
set(['today'])
。差异将是
set(['i','am','fine'])

这里有两个想法:

1.)编写一个函数,将两个集合的元素转换为小写,然后进行交集。这里有一种方法可以做到:

>>> intersect_with_key = lambda s1, s2, key=lambda i: i: set(map(key, s1)).intersection(map(key, s2))
>>> fs1 = frozenset('Today I am fine'.split())
>>> fs2 = frozenset('Hello how are you TODAY'.split())
>>> intersect_with_key(fs1, fs2)
set([])
>>> intersect_with_key(fs1, fs2, key=str.lower)
set(['today'])
>>>
但这不是很有效,因为每次调用都必须创建转换和新集合


2.)扩展
frozenset
类以保留元素的不区分大小写副本。重写
intersection
方法以使用元素的不区分大小写副本。这将更加有效。

不幸的是,即使您可以“动态更改”集合项的与比较相关的特殊方法(
\uuult\uuuuu
和朋友——实际上,只有
\ueq\uuuuu
需要集合当前的实现方式,但这是一个实现细节)——而且您不能,因为它们属于一种内置类型,
str
——这是不够的,因为
\uuuuuuuuuuuuuuuuuuuuuuu
也是至关重要的,而且在你想要进行交叉的时候它已经被应用了,将集合的项放在不同的散列桶中,从它们需要结束的地方开始,以使交叉点按您想要的方式工作(即,不保证“今天”和“今天”在同一个桶中)

这样,为了你的目的,你不可避免地需要建立新的数据结构——如果你认为必须这样做是“不雅”的,那么你就很不走运:内置的集合不承担巨大的负担和开销,这是为了让人们改变比较和散列函数所需要的,这会使事物膨胀10倍。(或更多)对于sae而言,在(可能)百万分之一的用例中感受到的需求

如果您经常需要与不区分大小写的比较连接,则应考虑子类或包<代码> STR (重写比较和哈希),以提供“不区分大小写的STR”类型<代码> CSTR < /C> >,当然,请确保仅将“代码> Cistr < /C> >的实例添加到您的集合(和C)中。感兴趣的(通过子类化

set
&c,或简单地通过支付费用)。给出一个过于简单的示例…:

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)

这确实会根据你表达的愿望发出
frozenset(['Today'])
。当然,在现实生活中,你可能想做更多的覆盖(例如…:按照我在这里的方式,对
cifrozenset
的任何操作都会返回一个普通的
frozenset
,失去了宝贵的独立于大小写的特殊功能--您可能希望确保每次都返回
cifrozenset
,而且,虽然非常可行,但这并不是小事).

以下是适用于任何一对iterables的版本:

def intersection(iterableA, iterableB, key=lambda x: x):
    """Return the intersection of two iterables with respect to `key` function.

    """
    def unify(iterable):
        d = {}
        for item in iterable:
            d.setdefault(key(item), []).append(item)
        return d

    A, B = unify(iterableA), unify(iterableB)

    return [(A[k], B[k]) for k in A if k in B]
例如:

print intersection('Today I am fine'.split(),
                   'Hello How a re you TODAY'.split(),
                   key=str.lower)
# -> [(['Today'], ['TODAY'])]
或者…用更少的地图

>>> a_ = set(map(str.lower, a))
>>> b_ = set(map(str.lower, b))
>>> a_ & b_
set(['today'])

我猜缓存self.lower()可能是一个有趣的第一次优化?对于给定的问题(将构建和交集包装到函数中,使用return而不是print),我给出的代码需要30 usec,缓存self.lower()44 usec。缓存散列也需要49 usec。更长(3x)的字符串的行为类似(即,没有缓存似乎工作得最好)。访问缓存值比动态重新计算要慢一点。是的,这意味着交叉点…doh。请注意,我在unicode文本上执行此操作时出现此错误:**TypeError:descriptor“lower”需要一个“str”对象,但收到了一个“unicode”我正在寻找解决方案。我将使用
d=collections.defaultdict(列表)
然后
d[key(item)]。追加(item)
@Mr_and_Mrs_d:是的。
defaultdict
非常适合这份工作(当时我可能在工作中使用了Python 2.4)。
print intersection('Today I am fine'.split(),
                   'Hello How a re you TODAY'.split(),
                   key=str.lower)
# -> [(['Today'], ['TODAY'])]
>>> a_, b_ = map(set, [map(str.lower, a), map(str.lower, b)])
>>> a_ & b_
set(['today'])
>>> a_ = set(map(str.lower, a))
>>> b_ = set(map(str.lower, b))
>>> a_ & b_
set(['today'])