Нечувствительный к регистру строковый класс в Python

1

Мне нужно выполнить сравнения строк без учета регистра в 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
    Вы уверены, что вам нужен класс? Почему бы вам просто не передать функцию сравнения при необходимости? Или хранить вещи lower Эд?
  • 0
    @Karoly Мне нужны исходные строки - передача функции comp приведет к меньшему количеству поддерживаемого кода
Показать ещё 9 комментариев
Теги:
string
python-2.7
case-insensitive

2 ответа

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.

(На основе qaru.site/questions/42092/...)

  • 0
    Это то, что я изначально думал - но есть много сложностей в создании такого класса пуленепробиваемым способом - так что даже конструктор будет хитрым - что даст CaseInsensitiveDict({'A': 1}) ?
  • 0
    Я предполагаю, что вам придется перебирать элементы в данном файле и преобразовывать каждый ключ в нужную форму. Если вы хотите что-то отличное от этого, вам решать, каковы ваши требования.
Показать ещё 2 комментария
1

В основном код выглядит нормально. Я бы устранил сокращение в __ge__, __le__ и __ne__ и расширил их, чтобы напрямую вызвать lower().

Кратковременное выражение похоже на то, что сделано в `functools.total_ordering(), но оно просто замедляет работу кода и затрудняет тестирование межтиповых сравнений, которые сложны, чтобы получить право, когда методы взаимозависимы.

  • 1
    К сожалению, в его нынешнем виде это ведет к неправильному поведению ( 'A' in d.keys() против 'A' in d ) - так принято @khelwood ответ - мне пришлось пойти дальше и написать обертку-обертку: stackoverflow.com/a/43457369 / 281545 . Комментарии более чем приветствуются :)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню