Нечувствительное к регистру сравнение наборов в 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 (переопределение сравнения и хеширование), чтобы обеспечить тип «без 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 возвращает простой frozenset , теряя специальную особенность независимого случая – вы Возможно, вы захотите убедиться, что 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'])