Строка строки, нечувствительная к регистру в python
Мне нужно выполнить сравнения строк без учета регистра в python в наборах и словарных клавишах. Теперь для создания наборов и подклассов dict, которые нечувствительны к регистру, оказывается удивительно сложным (см.: Нечувствительный к регистру словарь для идей, обратите внимание, что все они используют ниже - эй там даже отклоненный PEP, хотя его объем немного шире). Поэтому я пошел с созданием нечувствительного к регистру строкового класса (используя этот ответ by @AlexMartelli):
class CIstr(unicode):
"""Case insensitive with respect to hashes and comparisons string class"""
#--Hash/Compare
def __hash__(self):
return hash(self.lower())
def __eq__(self, other):
if isinstance(other, basestring):
return self.lower() == other.lower()
return NotImplemented
def __ne__(self, other): return not (self == other)
def __lt__(self, other):
if isinstance(other, basestring):
return self.lower() < other.lower()
return NotImplemented
def __ge__(self, other): return not (self < other)
def __gt__(self, other):
if isinstance(other, basestring):
return self.lower() > other.lower()
return NotImplemented
def __le__(self, other): return not (self > other)
Я полностью понимаю, что lower
не очень-то достаточно, чтобы охватить все случаи сравнения строк в юникоде, но я рефакторинг существующего кода, который использовал много clunkier-класс для сравнения строк (память и скорость), которые в любом случае использовали lower() - поэтому я могу изменить это на более позднем этапе - плюс я на python 2 (как видно на unicode
). Мои вопросы:
-
Я получил операторы правильно?
-
- этот класс достаточно для моих целей, учитывая, что я позабочусь о создании ключей в dicts и наборе элементов как экземпляры CIstr
- мои цели - проверка равенства, сдерживания, установки различий и аналогичных операций в нечувствительном к регистру путь. Или я что-то упускаю?
-
Стоит ли кэшировать строчную версию строки (как видно, например, в этом древнем рецепте python: Нечувствительные к регистру строки). Этот comment предполагает, что нет - плюс я хочу построить как можно быстрее и размер как можно меньше, но люди, похоже, включают это.
Советы по совместимости с Python 3 оценены!
Маленькая демонстрация:
d = {CIstr('A'): 1, CIstr('B'): 2}
print 'a' in d # True
s = set(d)
print {'a'} - s # set([])
Ответы
Ответ 1
В вашей демонстрации вы используете 'a'
, чтобы выглядеть в своем наборе. Это не сработает, если вы попытаетесь использовать 'a'
, потому что 'a'
имеет другой хеш. Также 'A' in d.keys()
будет true, но 'A' in d
будет ложным. Вы по существу создали тип, который нарушает нормальный контракт всех хэшей, утверждая, что он равен объектам с разными хэшами.
Вы могли бы объединить этот ответ с ответами о создании специализированных диктов и иметь dict, который преобразует любой возможный ключ в CIstr
, прежде чем пытаться его найти. Затем все ваши преобразования CIstr
могут быть скрыты внутри класса словаря.
например.
class CaseInsensitiveDict(dict):
def __setitem__(self, key, value):
super(CaseInsensitiveDict, self).__setitem__(convert_to_cistr(key), value)
def __getitem__(self, key):
return super(CaseInsensitiveDict, self).__getitem__(convert_to_cistr(key))
# __init__, __contains__ etc.
(На основе fooobar.com/questions/42092/...)
Ответ 2
В основном код выглядит нормально. Я бы устранил сокращение в __ge__
, __le__
и __ne__
и расширил их, чтобы напрямую вызвать lower().
Кратковременное выражение похоже на то, что сделано в `functools.total_ordering(), но оно просто замедляет работу кода и затрудняет тестирование межтиповых сравнений, которые сложны, чтобы получить право, когда методы взаимозависимы.