Büyük / küçük harfe duyarlı değil "içinde"


159

İfadeyi kullanmayı seviyorum

if 'MICHAEL89' in USERNAMES:
    ...

USERNAMESliste nerede .


Öğeleri büyük / küçük harf duyarlılığıyla eşleştirmenin herhangi bir yolu var mı yoksa özel bir yöntem kullanmam gerekiyor mu? Bunun için fazladan kod yazmaya gerek olup olmadığını merak ediyorum.

Yanıtlar:


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

Alternatif olarak:

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

Veya evet, özel bir yöntem yapabilirsiniz.


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

46
[...]tüm listeyi oluşturur. (name.upper() for name in USERNAMES)bir seferde yalnızca bir jeneratör ve gerekli bir dizi oluşturacaktır - bu işlemi çok yapıyorsanız büyük bellek tasarrufu. (her seferinde kontrol etmek için tekrar kullandığınız küçük harfli kullanıcı adlarının bir listesini oluşturursanız daha da fazla tasarruf)
viraptor

2
Performans nedenleriyle, dikteyi oluştururken tüm anahtarları düşürmeyi tercih edin.
Ryan 13

1
[x.lower () listedeki x için] bir liste anlayışı ise, (USERNAMES içindeki ad için name.upper ()) bir demet anlayışı mı? Yoksa başka bir adı mı var?
otocan

1
@otocan Bir üreteç ifadesidir.
nmichaels

21

Non-invaziv olabilmen için bir ambalaj yapardım . Asgari olarak, örneğin ...:

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

Şimdi, if CaseInsensitively('MICHAEL89') in whatever:gerektiği gibi davranmalıdır (sağ taraf bir liste, dikte veya küme olsun). (Dize dahil etme için benzer sonuçlar elde etmek, bazı durumlarda uyarılardan kaçınmak unicode, vb. İçin daha fazla çaba gerektirebilir ).


3
bu dikte için işe yaramaz, CaseInsensitively ('MICHAEL89') içinde {'Michael89': True}: yazdır "bulundu"
Xavier Combelle

2
Xavier: Bunun CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}çalışması için ihtiyacınız olacak , ki bu muhtemelen "gerektiği gibi davran" kapsamına girmez.
Gabe

O kadar ki, bunu yapmanın sadece 1 bariz yolu var. Çok kullanılmayacaksa bu ağır geliyor. Bu çok pürüzsüz dedi.
nmichaels

2
@Nathon, bana öyle geliyor ki, kabı istilacı olarak değiştirmek zorunda kalmak "ağır hissettiriyor" operasyonudur. Tamamen non-invaziv bir paketleyici: Bundan ne kadar "daha hafif" olabilir ki ?! Fazla değil;-). @Xavier, karışık harfli anahtarlara / öğelere sahip diktler veya setler olan RHS'lerin kendi invazif olmayan sarmalayıcılarına ihtiyacı var (kısa kısmın bir parçası etc.ve cevabımın "daha fazla çaba gerektiren" kısımları ;-).
Alex Martelli

Benim ağır tanımım, yalnızca bir kez kullanılacak, daha az sağlam ama çok daha kısa bir sürümün işe yarayacağı bir şey yapmak için epeyce kod yazmayı içerir. Bu birden fazla kullanılacaksa, son derece mantıklı.
nmichaels

14

Genellikle (en azından) nesnenizi istediğiniz gibi davranacak şekilde şekillendirirsiniz. name in USERNAMESbüyük / küçük harfe duyarlı değildir, bu nedenle USERNAMESdeğiştirilmesi gerekir:

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

Bununla ilgili harika olan şey, sınıfın dışında herhangi bir kodu değiştirmek zorunda kalmadan birçok iyileştirmenin yolunu açmasıdır. Örneğin, self.namesdaha hızlı aramalar için bir kümeye değiştirebilir veya (n.lower() for n in self.names)yalnızca bir kez hesaplayıp sınıfta depolayabilir ve bu şekilde devam edebilirsiniz ...


13

str.casefoldBüyük / küçük harfe duyarlı olmayan dize eşleştirme için önerilir. @ nmichaels'in çözümü önemsiz bir şekilde uyarlanabilir.

Either "kalıbını kullanınız:

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

Veya:

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

Gereğince docs :

Büyük-küçük harf ayırma, küçük harfe benzer ancak daha agresiftir çünkü bir dizedeki tüm büyük / küçük harf ayrımlarını kaldırmayı amaçlar. Örneğin, Alman küçük harfli 'ß', "ss" ile eşdeğerdir. Zaten küçük harf olduğundan, lower()'ß' için hiçbir şey yapmaz; casefold() bunu "ss" ye dönüştürür.


9

İşte bir yol:

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

Bu işlemin gerçekleşmesi için, hem string1ve string2nesneler türünde olmalıdır string.


5
AttributeError: 'list' nesnesinin 'alt' özniteliği yok
Jeff

@Jeff, öğelerinizden birinin bir liste olması ve her iki nesnenin de bir dizge olması gerektiğidir. Hangi nesne bir listedir?
Kullanıcı

1
Size oy verirdim, ancak cevabınızı değiştirmeden yapamam. Kesinlikle haklısın.
Jeff

@Jeff açıklama ekledim.
Kullanıcı

6

Sanırım fazladan bir kod yazmalısın. Örneğin:

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

Bu durumda, tüm girişlerin USERNAMESbüyük harfe dönüştürüldüğü yeni bir liste oluşturuyoruz ve ardından bu yeni listeyle karşılaştırıyoruz.

Güncelleme

Gibi @viraptor diyor, bunun yerine bir jeneratör kullanmak daha iyidir map. Bkz @Nathon 'ın cevabı .


Veya itertoolsişlevi kullanabilirsiniz imap. Bir jeneratörden çok daha hızlı ama aynı amacı gerçekleştiriyor.
buğdaylar

5

Yapabilirsin

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

Güncelleme: biraz oynadı ve kullanarak daha iyi bir kısa devre türü yaklaşımı elde edebileceğinizi düşünüyorum.

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

ifilterFonksiyon, itertools gelen Python içinde benim favori modüllerden biridir. Bir jeneratörden daha hızlıdır, ancak çağrıldığında yalnızca listenin sonraki öğesini oluşturur.


Sadece eklemek için, normal ifade kalıplarında spesifik bir anlamı olan ".", "?" Gibi karakterler içerebileceğinden, kalıbın öncelenmesi gerekebilir. bunu yapmak için re.escape (raw_string) kullanın
Iching Chang

0

5 (yanlış) sentim

"" içinde 'a' .join (['A']). daha düşük ()

GÜNCELLEME

Ah, tamamen katılıyorum @jpp, kötü uygulama örneği olarak tutacağım :(


2
Bu yanlış. OP'nin istediği bu olmadığında 'a' in "".join(['AB']).lower()geri dönüşleri düşünün True.
jpp

0

Buna liste yerine sözlük için ihtiyacım vardı, Jochen çözümü bu durum için en zarifti, bu yüzden biraz modifiye ettim:

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

şimdi bir sözlüğü böyle dönüştürebilir USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT)veif 'MICHAEL89' in USERNAMESDICT:


0

Tek satırda olması için yaptığım şey buydu:

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

Yine de zaman açısından test etmedim. Ne kadar hızlı / verimli olduğundan emin değilim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.