„W” bez rozróżniania wielkości liter

151

Uwielbiam używać tego wyrażenia

if 'MICHAEL89' in USERNAMES:
    ...

gdzie USERNAMESjest lista.


Czy istnieje sposób na dopasowanie elementów bez rozróżniania wielkości liter, czy też muszę użyć metody niestandardowej? Zastanawiam się tylko, czy istnieje potrzeba napisania dodatkowego kodu do tego.

RadiantHex
źródło

Odpowiedzi:

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

Alternatywnie:

if username.upper() in map(str.upper, USERNAMES):
    ...

Lub tak, możesz stworzyć niestandardową metodę.

nmichaels
źródło
8
if 'CaseFudge'.lower() in [x.lower() for x in list]
Fredley
44
[...]tworzy całą listę. (name.upper() for name in USERNAMES)stworzyłby tylko generator i jeden potrzebny ciąg na raz - ogromne oszczędności pamięci, jeśli wykonujesz tę operację dużo. (jeszcze więcej oszczędności, jeśli po prostu utworzysz listę nazw użytkowników
zapisanych
2
Wolę obniżyć wszystkie klawisze podczas budowania dyktowania, ze względu na wydajność.
Ryan
1
jeśli [x.lower () for x in list] jest zrozumieniem list, czy (name.upper () for name in USERNAMES) jest rozumieniem krotek? Czy ma inną nazwę?
otocan
1
@otocan To wyrażenie generatora.
nmichaels
21

Zrobiłbym opakowanie , abyś mógł być nieinwazyjny. Minimalnie, na przykład ...:

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

Teraz if CaseInsensitively('MICHAEL89') in whatever:powinien zachowywać się zgodnie z wymaganiami (niezależnie od tego, czy prawa strona jest listą, dyktowaniem czy zestawem). (Osiągnięcie podobnych wyników przy włączaniu ciągów może wymagać więcej wysiłku, unikaj ostrzeżeń w niektórych przypadkach unicode, itp.).

Alex Martelli
źródło
3
to nie działa w przypadku dyktowania, spróbuj, jeśli CaseInsensitively ('MICHAEL89') w {'Michael89': True}: print "found"
Xavier Combelle
2
Xavier: Potrzebowałbyś CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}, żeby to zadziałało, co prawdopodobnie nie oznacza „zachowuj się zgodnie z wymaganiami”.
Gabe
To tyle, jeśli chodzi o tylko jeden oczywisty sposób na zrobienie tego. To wydaje się ciężkie, chyba że będzie często używane. To powiedziawszy, jest bardzo płynne.
nmichaels
2
@Nathon, wydaje mi się, że inwazyjna zmiana pojemnika jest operacją „czuje się ciężko”. Całkowicie nieinwazyjne opakowanie: o ile „lżejsze” niż to można dostać ?! Niewiele;-). @Xavier, RHS, które są dyktami lub zestawami z kluczami / przedmiotami o mieszanej wielkości, potrzebują własnych nieinwazyjnych opakowań (część krótkiej etc.i wymagającej więcej wysiłku części mojej odpowiedzi ;-).
Alex Martelli
Moja definicja ciężkiego polega na napisaniu sporej ilości kodu, aby stworzyć coś, co zostanie użyte tylko raz, gdzie wystarczyłaby mniej solidna, ale znacznie krótsza wersja. Jeśli będzie używany więcej niż raz, jest to całkowicie rozsądne.
nmichaels
12

Zwykle (przynajmniej w sumie) kształtujesz obiekt tak, aby zachowywał się tak, jak chcesz. name in USERNAMESnie jest rozróżniana wielkość liter, dlatego USERNAMESnależy zmienić:

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

Wspaniałą rzeczą w tym jest to, że otwiera drogę do wielu ulepszeń, bez konieczności zmiany kodu poza klasą. Na przykład możesz zmienić self.nameszestaw na zestaw w celu szybszego wyszukiwania lub obliczyć (n.lower() for n in self.names)jedyny raz i zapisać go w klasie i tak dalej ...

Jochen Ritzel
źródło
10

str.casefoldjest zalecany do dopasowywania ciągów bez uwzględniania wielkości liter. Rozwiązanie @nmichaels można w prosty sposób dostosować.

Użyj:

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

Lub:

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

Zgodnie z dokumentacją :

Zwijanie liter jest podobne do małych liter, ale bardziej agresywne, ponieważ ma na celu usunięcie wszystkich rozróżnień wielkości liter w ciągu. Na przykład niemiecka mała litera „ß” jest odpowiednikiem „ss”. Ponieważ jest już zapisane małymi literami, lower()nic nie zrobi z „ß”; casefold() konwertuje go na „ss”.

jpp
źródło
8

Oto jeden sposób:

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

Aby to zadziałało, oba obiekty string1i string2obiekty muszą być typu string.

Użytkownik
źródło
5
AttributeError: obiekt „list” nie ma atrybutu „niższy”
Jeff
@Jeff to dlatego, że jeden z twoich elementów jest listą, a oba obiekty powinny być ciągiem. Który obiekt to lista?
Użytkownik
1
Głosowałbym na Ciebie, ale nie mogę, chyba że zmienisz swoją odpowiedź. Masz absolutną rację.
Jeff
@Jeff Dodałem wyjaśnienie.
Użytkownik
6

Myślę, że musisz napisać dodatkowy kod. Na przykład:

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

W tym przypadku tworzymy nową listę ze wszystkimi wpisami w formacie USERNAMES zamienionymi na duże litery, a następnie porównujemy z tą nową listą.

Aktualizacja

Jak mówi @viraptor , jeszcze lepiej jest użyć generatora zamiast map. Zobacz @Nathon „s odpowiedź .

Manoj Govindan
źródło
Lub możesz użyć itertoolsfunkcji imap. Jest znacznie szybszy niż generator, ale spełnia ten sam cel.
pszeniczne
5

Mógłbyś

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

Aktualizacja: trochę się pobawiłem i myślę, że możesz uzyskać lepsze podejście do typu zwarcia przy użyciu

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

ifilterFunkcja jest z itertools, jeden z moich ulubionych w obrębie modułów Pythona. Jest szybszy niż generator, ale po wywołaniu tworzy tylko następny element listy.

pszenicy
źródło
Wystarczy dodać, że wzorzec może wymagać zmiany znaczenia, ponieważ może zawierać znaki takie jak „.”, „?”, Co ma szczególne znaczenie we wzorcach wyrażeń regularnych. użyj re.escape (raw_string), aby to zrobić
Iching Chang
0

Moje 5 (źle) centów

„a” w „” .join ([„A”]). lower ()

AKTUALIZACJA

Ouch, całkowicie się zgadzam @jpp, podam jako przykład złej praktyki :(

GBrian
źródło
2
To jest źle. Rozważ 'a' in "".join(['AB']).lower()zwroty, Truegdy nie tego chce OP.
jpp
0

Potrzebowałem tego do słownika zamiast listy, rozwiązanie Jochena było najbardziej eleganckie w tym przypadku, więc trochę go zmodyfikowałem:

class CaseInsensitiveDict(dict):
    ''' requests special dicts are case insensitive when using the in operator,
     this implements a similar behaviour'''
    def __contains__(self, name): # implements `in`
        return name.casefold() in (n.casefold() for n in self.keys())

teraz możesz przekonwertować taki słownik USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT)i użyćif 'MICHAEL89' in USERNAMESDICT:

Megarushing
źródło
0

Aby mieć to w jednej linii, zrobiłem to:

if any(([True if 'MICHAEL89' in username.upper() else False for username in USERNAMES])):
    print('username exists in list')

Nie testowałem tego jednak pod względem czasu. Nie jestem pewien, jak szybko / wydajnie jest.

MFA
źródło