Без учета регистра 'in' - Python - PullRequest
129 голосов
/ 02 сентября 2010

Я люблю использовать выражение

if 'MICHAEL89' in USERNAMES:
    ...

, где USERNAMES - список


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

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

Ответы [ 8 ]

152 голосов
/ 02 сентября 2010
if 'MICHAEL89' in (name.upper() for name in USERNAMES):
    ...

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

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

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

18 голосов
/ 02 сентября 2010

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

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: должен вести себя так, как требуется (независимо от того, является ли правая часть списком, комментарием или набором). (Может потребоваться больше усилий для достижения аналогичных результатов при включении строк, избегать предупреждений в некоторых случаях, касающихся unicode и т. Д.).

10 голосов
/ 02 сентября 2010

Обычно (по крайней мере, в oop) вы формируете свой объект так, как вам хочется.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) только один раз и сохранить его в классе и так далее ...

6 голосов
/ 02 августа 2014

Вот один из способов:

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

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

6 голосов
/ 02 сентября 2010

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

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

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

Обновление

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

4 голосов
/ 10 августа 2018

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

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

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

Или:

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

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

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

4 голосов
/ 02 сентября 2010

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

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. Это быстрее, чем генератор, но создает только следующий элемент списка при вызове.

0 голосов
/ 15 марта 2019

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

'a' in "" .join (['A']). Lower ()

UPDATE

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

...