Нечувствительное к регистру сравнение наборов в 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'])]
- python – быстрый способ сравнения списков и поиска термина в списке
- сравнение строк в питоне, но не расстояние Левенштейна (я думаю)
- Использование python для поиска повторяющихся имен в txt-файле
- python сравнивает элементы считывателя dict из двух файлов csv
- Как сравнить результаты двух файлов CSV в Python с DictReader или Pandas? (Открыта для любых других методов!)
Вот версия, которая работает для любой пары итераций:
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'])
- Словарь-подобный объект в Python, который позволяет устанавливать произвольные атрибуты
- как «установить» настраиваемый модуль python, сделанный boost.python
- python, как найти, если словарь содержит данные из другого словаря
- как сравнить для равенства для объектов None в пользовательском классе в python?
- Как скопировать данные в Python
- (На английском, Perl, Python, Ruby) сравнение по фрагменту по фрагменту кода?
- Pandon Pandas Если значение в столбце B = равно , замените столбец A на «T»
- TypeError: NoneType, может сравниваться, если он равен, ошибка, когда больше, чем
- Сравнение рядов матричных чисел
- Сравнение двух полей фильтра django
- Поиск несоответствий в кортежах и объединение их в Python