Maiuscole / minuscole "in"


151

Adoro usare l'espressione

if 'MICHAEL89' in USERNAMES:
    ...

dov'è USERNAMESun elenco.


Esiste un modo per abbinare gli articoli con insensibilità al maiuscolo o devo usare un metodo personalizzato? Mi chiedo solo se è necessario scrivere un codice aggiuntivo per questo.

Risposte:


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

In alternativa:

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

Oppure, sì, puoi creare un metodo personalizzato.


8
if 'CaseFudge'.lower() in [x.lower() for x in list]
Fredley,

44
[...]crea l'intero elenco. (name.upper() for name in USERNAMES)creerebbe solo un generatore e una stringa necessaria alla volta - enormi risparmi di memoria se si esegue questa operazione molto. (ancora più risparmi, se crei semplicemente un elenco di nomi utente minuscoli che riutilizzi per il controllo ogni volta)
viraptor

2
Preferisce abbassare tutti i tasti durante la creazione del dict, per motivi di prestazioni.
Ryan,

1
se [x.lower () per x in list] è una comprensione di lista, (name.upper () per name in USERNAMES) è una comprensione di tupla? O ha un altro nome?
otocan,

1
@otocan È un'espressione del generatore.
nmichaels,

21

Vorrei fare un involucro in modo da poter essere non invasivo. In minima parte, ad esempio ...:

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

Ora, if CaseInsensitively('MICHAEL89') in whatever:dovrebbe comportarsi come richiesto (se il lato destro è un elenco, dettare o impostare). (Potrebbe essere necessario uno sforzo maggiore per ottenere risultati simili per l'inclusione delle stringhe, evitare avvisi in alcuni casi che coinvolgono unicode, ecc.).


3
che non funziona per dict provare se CaseInsensitively ('MICHAEL89') in {'Michael89': True}: print "found"
Xavier Combelle,

2
Xavier: Avresti bisogno CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}che funzioni, che probabilmente non rientra nel "comportarsi come richiesto".
Gabe,

Tanto per esserci solo 1 modo ovvio per farlo. Sembra pesante a meno che non venga usato molto. Detto questo, è molto fluido.
nmichaels,

2
@Nathon, mi sembra che dover modificare invasivamente il container sia l'operazione "sembra pesante". Un involucro completamente non invasivo: quanto "più leggero" di questo si potrebbe ottenere ?! Non tanto;-). @Xavier, RHS che sono dicts o set con chiavi / oggetti in maiuscole / minuscole hanno bisogno dei propri involucri non invasivi (parte della breve etc.e "richiedono più sforzo" parti della mia risposta ;-).
Alex Martelli,

La mia definizione di pesante implica la scrittura di un bel po 'di codice per creare qualcosa che verrà usato solo una volta, dove farebbe una versione meno robusta ma molto più breve. Se questo verrà usato più di una volta, è perfettamente sensato.
nmichaels,

12

Di solito (almeno in oop) dai forma al tuo oggetto per comportarti nel modo che preferisci. name in USERNAMESnon fa distinzione tra maiuscole e minuscole, quindi USERNAMESdeve cambiare:

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

Il bello di questo è che apre la strada a molti miglioramenti, senza dover cambiare alcun codice al di fuori della classe. Ad esempio, potresti cambiare il self.namesset in un set per ricerche più veloci, o calcolare la (n.lower() for n in self.names)sola volta e memorizzarlo nella classe e così via ...


10

str.casefoldè consigliato per la corrispondenza di stringhe senza distinzione tra maiuscole e minuscole. La soluzione di @ nmichaels può essere banalmente adattata.

Usa uno di questi:

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

O:

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

Secondo i documenti :

Il casefolding è simile al minuscolo ma più aggressivo perché è destinato a rimuovere tutte le distinzioni del caso in una stringa. Ad esempio, la lettera minuscola tedesca 'ß' equivale a "ss". Dato che è già in minuscolo, lower()non farebbe nulla per "ß"; casefold() lo converte in "ss".


8

Ecco un modo:

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

Affinché ciò funzioni, entrambi string1e gli string2oggetti devono essere di tipo string.


5
AttributeError: l'oggetto 'list' non ha attributi 'lower'
Jeff

@Jeff perché uno dei tuoi elementi è un elenco ed entrambi gli oggetti dovrebbero essere una stringa. Quale oggetto è un elenco?
Utente

1
Vorrei votarti, ma non posso se non modifichi la tua risposta. Hai assolutamente ragione.
Jeff

@Jeff ho aggiunto chiarimenti.
Utente

6

Penso che tu debba scrivere del codice extra. Per esempio:

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

In questo caso stiamo formando un nuovo elenco con tutte le voci USERNAMESconvertite in maiuscolo e confrontandole con questo nuovo elenco.

Aggiornare

Come dice @viraptor , è ancora meglio usare un generatore invece di map. Vedi la risposta di @Nathon .


Oppure potresti usare la itertoolsfunzione imap. È molto più veloce di un generatore ma raggiunge lo stesso obiettivo.
Wheaties

5

Si potrebbe fare

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

Aggiornamento: ho giocato un po 'e sto pensando che potresti ottenere un migliore approccio al tipo di corto circuito usando

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

La ifilterfunzione è di itertools, uno dei miei moduli preferiti all'interno di Python. È più veloce di un generatore ma crea l'elemento successivo dell'elenco solo quando richiesto.


Solo per aggiungere, potrebbe essere necessario sfuggire al modello, poiché potrebbe contenere caratteri come ".", "?", Che ha un significato specail nei modelli di espressioni regolari. usa re.escape (raw_string) per farlo
Iching Chang

0

I miei 5 centesimi (sbagliati)

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

AGGIORNARE

Ahi, sono totalmente d'accordo @jpp, terrò come esempio di cattiva pratica :(


2
Questo è sbagliato. Considera i 'a' in "".join(['AB']).lower()ritorni Truequando questo non è ciò che OP desidera.
jpp,

0

Ne avevo bisogno per un dizionario anziché per un elenco, la soluzione Jochen era la più elegante per quel caso, quindi l'ho modificata un po ':

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())

ora puoi convertire un dizionario in questo modo USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT)e usarloif 'MICHAEL89' in USERNAMESDICT:


0

Per averlo in una riga, questo è quello che ho fatto:

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

Non l'ho provato per tempo. Non sono sicuro di quanto sia veloce / efficiente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.