[英]Case-insensitive comparison of sets in Python
我有兩套(雖然我可以做清單,或其他):
a = frozenset(('Today','I','am','fine'))
b = frozenset(('hello','how','are','you','today'))
我想得到:
frozenset(['Today'])
或至少:
frozenset(['today'])
第二種選擇是可行的,如果我小寫我認為的一切,但我正在尋找一種更優雅的方式。 有可能嗎?
a.intersection(b)
以不區分大小寫的方式?
Django中的快捷方式也很好,因為我正在使用該框架。
下面的交集方法示例(我無法弄清楚如何在注釋中獲取此格式):
print intersection('Today I am fine tomorrow'.split(),
'Hello How a re you TODAY and today and Today and Tomorrow'.split(),
key=str.lower)
[(['tomorrow'], ['Tomorrow']), (['Today'], ['TODAY', 'today', 'Today'])]
這里的版本適用於任何一對迭代:
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'])]
不幸的是,即使你可以“隨時改變”集合項目的比較相關的特殊方法( __lt__
和朋友 - 實際上,只有__eq__
需要當前實現集合的方式,但這是一個實現細節) - 和你不能,因為他們屬於內置型, str
-這是不夠的,因為__hash__
也很重要,你所要做的交集它已經被應用的時候,把套在項目不同的散列桶,他們需要最終以你想要的方式使交叉點工作(即,不能保證'今天'和'今天'在同一個桶中。
所以,為了你的目的,你不可避免地需要建立新的數據結構 - 如果你認為它“不優雅”必須要做到這一點,那你就好運了:內置的套裝只是不隨身攜帶允許人們改變比較和散列函數所需的巨額行李和開銷,這將使得需要的事情膨脹10倍(或更多),並且可能在一百萬個用例中感受到。
如果您經常需要使用不區分大小寫的比較,則應考慮子類化或包裝str
(重寫比較和散列)以提供“不區分大小寫的str”類型cistr
- 然后,當然,請確保只有cistr
實例(例如)將(例如)添加到您感興趣的集合(&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
上的任何操作cifrozenset
返回一個簡單的frozenset
,失去珍貴的案例獨立特征 - 你我可能希望確保cifrozenset
都返回一個cifrozenset
,並且,雖然非常可行,但這並非無足輕重。
首先,你不是指a.intersection(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
方法以使用元素的不區分大小寫的副本。 這會更有效率。
>>> 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'])
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.