Нечувствительный к регистру 'in' - Python

Мне нравится использовать выражение

if 'MICHAEL89' in USERNAMES:
    ...

где USERNAMES - список


Есть ли способ сопоставить элементы с нечувствительностью к регистру или мне нужно использовать собственный метод? Просто интересно, нужно ли писать дополнительный код для этого.

Спасибо всем!

Ответ 1

if 'MICHAEL89' in (name.upper() for name in USERNAMES):
    ...

В качестве альтернативы:

if 'MICHAEL89' in map(str.upper, USERNAMES):
    ...

Или, да, вы можете создать собственный метод.

Ответ 2

Я бы сделал обертку, чтобы вы не были инвазивны. Минимально, например...:

class CaseInsensitively(object):
    def __init__(self, s):
        self.__s = s.lower()
    def __hash__(self):
        return hash(self.__s)
    def __eq__(self, other):
        # ensure proper comparison between instances of this class
        try:
           other = other.__s
        except (TypeError, AttributeError):
          try:
             other = other.lower()
          except:
             pass
        return self.__s == other

Теперь if CaseInsensitively('MICHAEL89') in whatever: должен вести себя по мере необходимости (есть ли правая часть - список, dict или set). (Может потребоваться больше усилий для достижения аналогичных результатов для включения строк, во избежание предупреждений в некоторых случаях с участием unicode и т.д.).

Ответ 3

Обычно (по крайней мере, по крайней мере) вы формируете свой объект, чтобы вести себя так, как вы хотите. name in USERNAMES нечувствителен к регистру, поэтому USERNAMES необходимо изменить:

class NameList(object):
    def __init__(self, names):
        self.names = names

    def __contains__(self, name): # implements `in`
        return name.lower() in (n.lower() for n in self.names)

    def add(self, name):
        self.names.append(name)

# now this works
usernames = NameList(USERNAMES)
print someone in usernames

Самое замечательное в том, что он открывает путь для многих улучшений без необходимости изменять какой-либо код вне класса. Например, вы можете изменить self.names на набор для более быстрого поиска или вычислить (n.lower() for n in self.names) только один раз и сохранить его в классе и так далее...

Ответ 4

Думаю, вам нужно написать дополнительный код. Например:

if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
   ...

В этом случае мы формируем новый список со всеми элементами в USERNAMES, преобразованными в верхний регистр, а затем сравниваем с этим новым списком.

Обновить

Как @viraptor говорит, что лучше использовать генератор вместо map. См. @Nathon .

Ответ 5

Вы могли бы сделать

matcher = re.compile('MICHAEL89', re.IGNORECASE)
filter(matcher.match, USERNAMES) 

Обновление: немного поиграл, и я думаю, что вы можете получить лучший подход к типу короткого замыкания, используя

matcher = re.compile('MICHAEL89', re.IGNORECASE)
if any( ifilter( matcher.match, USERNAMES ) ):
    #your code here

Функция ifilter - это itertools, один из моих любимых модулей в Python. Это быстрее, чем генератор, но создает только следующий элемент списка при вызове.

Ответ 6

str.casefold рекомендуется для сопоставления строк без str.casefold регистра. Решение @nmichaels можно легко адаптировать.

Используйте либо:

if 'MICHAEL89'.casefold() in (name.casefold() for name in USERNAMES):

Или же:

if 'MICHAEL89'.casefold() in map(str.casefold, USERNAMES):

Согласно документам:

Свертывание регистров похоже на нижний регистр, но более агрессивно, потому что оно предназначено для удаления всех различий регистра в строке. Например, немецкая строчная буква "ß" эквивалентна "ss". Так как это уже строчные буквы, lower() ничего не будет делать с 'ß'; casefold() преобразует его в "ss".

Ответ 7

Здесь один из способов:

if string1.lower() in string2.lower(): 
    ...

Чтобы это работало, оба объекта string1 и string2 должны иметь тип string.

Ответ 8

Мои 5 (неправильных) центов

'a' в "".join(['A']). lower()

ОБНОВИТЬ

Ой, полностью согласен @jpp, я приведу пример плохой практики :(